In many web frameworks, insecure or incomplete default configurations can lead to subtle weaknesses. Skilled penetration testers or bug bounty hunters can leverage these misconfigurations, often chaining them with other issues, to develop real-world exploits and escalate their impact.
During an internal penetration test, I encountered what initially appeared to be a CSRF misconfiguration in a Django-based application. However, after deeper analysis, this behavior turned out to be expected by design, though it can still introduce risks if misunderstood or improperly configured.
This post clarifies how Django handles CSRF tokens, what might look like a vulnerability, and what security considerations you should keep in mind.
—
While testing a Django application, I observed the following:
csrftoken (stored in cookies) appeared to be identical to the csrfmiddlewaretoken (sent in POST requests).11111111111111111111111111111111), the request was still accepted.Press enter or click to view image in full size
At first glance, this behavior suggests that any value could bypass CSRF protection, which would be a important issue.
—
In Django, CSRF protection is implemented using a double submit cookie pattern with masking.
csrftoken (Cookie)csrfmiddlewaretoken (Form / POST body)—
Each time a page is rendered with {% csrf_token %}:
csrftoken cookie.csrfmiddlewaretoken = mask + masked_secretThis ensures that the token value changes on every request, mitigating compression-based attacks such as BREACH.
If both the cookie and the request token are manually set to the same arbitrary value, Django will accept the request.
This is because:
Django only verifies that the two tokens match — it does not validate whether the token was originally issued by the server.
This is not a bug, but rather a characteristic of the double submit cookie pattern.
Join Medium for free to get updates from this writer.
—
Although this behavior is expected, it introduces risk under certain conditions:
Since the CSRF token is stored in a cookie:
Even though Django mitigates BREACH via masking, improper handling or custom implementations could reintroduce this risk.
If an attacker can execute JavaScript:
HttpOnly is set)—
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
...
]CSRF_COOKIE_SECURE = True # HTTPS only
CSRF_COOKIE_HTTPONLY = True # Client-side JavaScript will not be able to access the CSRF cookie.
CSRF_COOKIE_SAMESITE = 'Strict' # Strict same-site
CSRF_USE_SESSIONS = True # Store the CSRF token in the user's session instead of in a cookie.
Press enter or click to view image in full size
csrftoken and csrfmiddlewaretoken represent the same underlying secret, just in different forms.What initially appears to be a CSRF bypass is actually a design trade-off in Django’s implementation.
However, in real-world scenarios, this behavior can become exploitable when combined with:
Understanding these nuances is critical — not only for identifying real vulnerabilities, but also for avoiding false positives during security assessments.