Django CSRF Token Security Explained

Django's CSRF token changes on every single form render. The one in the HTML is never identical to the one in the cookie. That's not a bug, it's the whole point. Here's what actually happens: 1. Django stores a 32-character random secret in the CSRF cookie. That secret never changes across the session. 2. What goes into the form is a masked token. It is a fresh 32-character salt concatenated with the XOR of that salt and the secret. 64 characters total, different every render. 3. On every unsafe request (POST, PUT, PATCH, DELETE), CsrfViewMiddleware.process_view() intercepts. It extracts the salt from the submitted token, XORs back to recover the embedded secret, then compares that to the cookie secret. 4. The tokens never match directly. The secrets do. This masking exists to defeat BREACH. It is a compression-based attack where seeing the same ciphertext repeatedly across responses leaks the underlying secret over time. The trap: - setting CSRF_COOKIE_HTTPONLY = True makes the cookie unreadable by JavaScript. - SPAs that read the token via document.cookie and inject it as X-CSRFToken silently break. Every POST returns 403. The CSRF token is a cryptographic proof that the sender could read a cookie. The XOR masking is what every tutorial skips and what actually makes that proof hold. What's the most confusing CSRF failure that turned out to be a config issue rather than a code bug? #Python #Django #BackendDevelopment #WebSecurity

  • text

Can I ask what about packages like django-allauth.headless where the token is not HTTPONLY?

Like
Reply

To view or add a comment, sign in

Explore content categories