Search code examples
pythondjangocookiescsrf

How would changing Django's cookie domain settings cause sporadic CSRF failures?


In my Django project, until recently I had left the settings SESSION_COOKIE_DOMAIN and CSRF_COOKIE_DOMAIN unset. Recently I changed them to .mydomain.com and since then I have been seeing sporadic CSRF failures on AJAX requests for certain users. The failure manifests as a 403 with CSRF token missing or incorrect. appearing in the logs.

Asking users to clear cookies seems to resolve the issue, but I'm wondering how the settings change could have caused this and why it only seems to be happening for some users and not others.

Wondering if there is a way to resolve these issues without asking my users to clear cookies.


Solution

  • The cookie with the new SESSION_COOKIE_DOMAIN is sent as a new cookie and does not replace the old one. So the browser will send both to your server. AFAICT, it sends them in arbitrary order.

    That means that you're setting a cookie for .mydomain.com, but receiving either the cookie you just set for .mydomain.com, or a stale cookie for whatever.mydomain.com was implicitly set originally (because django will only pick one, most likely the last it sees). Which one you get depends on the browser, possibly on some particulars of how the client computer stores them, and possibly even on how django reads the headers. This is why the failures are inconsistent: it randomly works for some clients and fails for others.

    Edit: You could delete the stale cookie from the server, if you know the original cookie's properties. Probably the best way is to set a custom Set-Cookie header with the domain and other properties set, and expiration date in the past. You could do that e.g. from the 403 page handler. (see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie and Correct way to delete cookies server-side )