Cross-site request forgery (CSRF) forces an authenticated user to perform unintended state-changing actions in a web application. This rule detects when CSRF protection is explicitly disabled or missing from an application.

Why is this an issue?

When CSRF protection is disabled or bypassed, an attacker can trick a logged-in user into submitting requests the application treats as authenticated. The rule flags configurations that disable framework CSRF middleware, exempt specific routes or views, or leave unsafe HTTP methods unprotected.

What is the potential impact?

Unauthorized state changes

An attacker can change passwords, transfer funds, modify data, or perform other privileged operations using the victim’s session.

Account compromise

Successful CSRF attacks can lead to full account takeover when combined with sensitive actions such as email or credential changes.

How to fix it in Django

Enable django.middleware.csrf.CsrfViewMiddleware in settings and do not exempt views from CSRF protection unless strictly necessary.

Code examples

Disabling or bypassing CSRF protection allows an authenticated user’s browser to execute state-changing requests the user did not intend.

Noncompliant code example

django.middleware.csrf.CsrfViewMiddleware is not used in the Django settings:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
] # Noncompliant: django.middleware.csrf.CsrfViewMiddleware is missing

The CSRF protection is disabled on a view:

@csrf_exempt # Noncompliant
def example(request):
    return HttpResponse("default")

Compliant solution

Protect all views with django.middleware.csrf.CsrfViewMiddleware:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Do not disable the CSRF protection on specific views:

def example(request):
    return HttpResponse("default")

How to fix it in Flask

Use the CSRFProtect module from Flask-WTF and keep WTF_CSRF_ENABLED enabled.

Code examples

Disabling or bypassing CSRF protection allows an authenticated user’s browser to execute state-changing requests the user did not intend.

Noncompliant code example

The WTF_CSRF_ENABLED setting is set to false:

app = Flask(__name__)
app.config['WTF_CSRF_ENABLED'] = False # Noncompliant

The application doesn’t use the CSRFProtect module:

app = Flask(__name__) # Noncompliant: CSRFProtect is missing

@app.route('/')
def hello_world():
    return 'Hello, World!'

The CSRF protection is disabled on a view:

@app.route('/example/', methods=['POST'])
@csrf.exempt # Noncompliant
def example():
    return 'example '

The CSRF protection is disabled on a form:

class unprotectedForm(FlaskForm):
    class Meta:
        csrf = False # Noncompliant

    name = TextField('name')
    submit = SubmitField('submit')

Compliant solution

Use the CSRFProtect module (and do not disable it with WTF_CSRF_ENABLED set to false):

app = Flask(__name__)
csrf = CSRFProtect()
csrf.init_app(app)
app = Flask(__name__)
csrf = CSRFProtect()
csrf.init_app(app)

Do not disable the CSRF protection on specific views or forms:

@app.route('/example/', methods=['POST'])
def example():
    return 'example '
class unprotectedForm(FlaskForm):
    class Meta:
        csrf = True

    name = TextField('name')
    submit = SubmitField('submit')

Resources

Documentation

Articles & blog posts

Standards