v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
MEDIUM AI PREVALENCE: HIGH auth.py.flow.csrf-exempt

A Django view disables CSRF protection.

Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.

Why this matters

The @csrf_exempt decorator (from django.views.decorators.csrf), or @method_decorator(csrf_exempt, ...) on a class-based view, turns off Django's CSRF middleware check for that endpoint. An attacker can then forge cross-site requests that the victim's browser submits with their session cookie, a CSRF vulnerability.

Do not exempt views from CSRF. Keep the default protection and submit the CSRF token from your client. For machine-to-machine endpoints such as webhooks, validate a signed request signature (e.g. an HMAC header) instead of disabling CSRF wholesale.

VULNERABLE
vulnerable.py
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST


# ruleid: auth.py.flow.csrf-exempt
@csrf_exempt
def webhook(request):
    return JsonResponse({"ok": True})


# ruleid: auth.py.flow.csrf-exempt
@csrf_exempt
@require_POST
def receive_event(request):
    return JsonResponse({"ok": True})


# ruleid: auth.py.flow.csrf-exempt
@method_decorator(csrf_exempt, name="dispatch")
class WebhookView(View):
    def post(self, request):
        return JsonResponse({"ok": True})
SAFE
safe.py
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.views.decorators.http import require_POST


# ok: auth.py.flow.csrf-exempt -- protected by default CSRF middleware, no exemption
def submit(request):
    return JsonResponse({"ok": True})


# ok: auth.py.flow.csrf-exempt -- importing csrf_exempt without applying it is fine
@require_POST
def receive_event(request):
    return JsonResponse({"ok": True})


# ok: auth.py.flow.csrf-exempt -- CSRF protection explicitly enforced
@csrf_protect
def update_profile(request):
    return JsonResponse({"ok": True})


# ok: auth.py.flow.csrf-exempt -- class-based view with default protection
@method_decorator(csrf_protect, name="dispatch")
class ProfileView(View):
    def post(self, request):
        return JsonResponse({"ok": True})

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

# oauthlint-disable-next-line auth.py.flow.csrf-exempt -- <reason>

References

https://docs.djangoproject.com/en/stable/ref/csrf/ ↗https://cwe.mitre.org/data/definitions/352.html ↗