Django CSRF Token: Misconfiguration or Misunderstanding?
quality 7/10 · good
0 net
Django CSRF Token: Misconfiguration or Misunderstanding? | by Windasunny | in InfoSec Write-ups - Freedium
Milestone: 20GB Reached
We’ve reached 20GB of stored data — thank you for helping us grow!
Patreon
Ko-fi
Liberapay
Close
< Go to the original
Django CSRF Token: Misconfiguration or Misunderstanding?
In many web frameworks, insecure or incomplete default configurations can lead to subtle weaknesses. Skilled penetration testers or bug…
Windasunny
Follow
InfoSec Write-ups
·
~3 min read
·
March 26, 2026 (Updated: March 26, 2026)
·
Free: Yes
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.
—
Observation
While testing a Django application, I observed the following:
The csrftoken (stored in cookies) appeared to be identical to the csrfmiddlewaretoken (sent in POST requests).
Even when replacing both values with a completely arbitrary string (e.g., 11111111111111111111111111111111 ), the request was still accepted.
The token persisted across the session without being invalidated.
source from real website, mask almost all sensitive information
At first glance, this behavior suggests that any value could bypass CSRF protection , which would be a important issue.
—
Understanding Django's CSRF Design
In Django, CSRF protection is implemented using a double submit cookie pattern with masking .
Token Components
csrftoken (Cookie)
Stores the original CSRF secret .
csrfmiddlewaretoken (Form / POST body)
Contains a masked version of the same secret.
—
Token Generation
Each time a page is rendered with {% csrf_token %} :
Django generates (or reuses) a secret.
The secret is stored in the csrftoken cookie.
A masked version of the token is embedded in the HTML form: csrfmiddlewaretoken = mask + masked_secret
This ensures that the token value changes on every request, mitigating compression-based attacks such as BREACH.
Why It's Still Passed
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.
—
Security Implications
Although this behavior is expected, it introduces risk under certain conditions:
1. Token Stored in Cookie
Since the CSRF token is stored in a cookie:
It may be exposed via XSS
It may be leaked through browser extensions or misconfigurations
2. BREACH Attack
Even though Django mitigates BREACH via masking, improper handling or custom implementations could reintroduce this risk.
3. XSS + CSRF Combination
If an attacker can execute JavaScript:
They can read the CSRF token (unless HttpOnly is set)
They can forge valid requests
—
Recommended Hardening
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.
This is my test website
Key Takeaways
csrftoken and csrfmiddlewaretoken represent the same underlying secret , just in different forms.
Django uses masking to prevent BREACH attacks.
The system relies on token consistency , not token origin validation.
This design is secure only if combined with proper cookie and browser protections .
Final Thoughts
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:
XSS vulnerabilities
Misconfigured cookies
Weak SameSite policies
Understanding these nuances is critical — not only for identifying real vulnerabilities, but also for avoiding false positives during security assessments.
Reference
Django CSRF Documentation:
https://docs.djangoproject.com/en/6.0/ref/csrf/
Reporting a Problem
Sometimes we have problems displaying some Medium posts.
If you have a problem that some images aren't loading - try using VPN. Probably you have problem with
access to Medium CDN (or fucking Cloudflare's bot detection algorithms are blocking you).