#33529 closed Bug (invalid)
Possibly Incorrect statement in the docs about CRSF tokens
Reported by: | Michael | Owned by: | nobody |
---|---|---|---|
Component: | contrib.auth | Version: | 4.0 |
Severity: | Normal | Keywords: | |
Cc: | Florian Apolloner, Shai Berger | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
https://docs.djangoproject.com/en/4.0/ref/csrf/ says:
Note
The CSRF token is also present in the DOM, but only if explicitly included using csrf_token in a template. The cookie contains the canonical token; the CsrfViewMiddleware will prefer the cookie to the token in the DOM. Regardless, you’re guaranteed to have the cookie if the token is present in the DOM, so you should use the cookie!
How from my testing, I think it prefers the DOM token to the cookie. You can prove this behaviour as follows:
- Log out
- Open two tabs to the login screen; tab A and tab B
- Log in on tab A, this will destroy the Anonymous session, and create a new session.
- Then switch to tab B and Log in
- You will get a CSRF Error because it is using the DOM token linked to the anonymous sessions (that was destroyed), not the cookie.
I think instead of correcting the docs, the cookie first behaviour is desired. However with the cookie first approach, why does one need the CSRF DOM token ({% csrftoken %}
)?
I can only think of the unusual case when one is performing AJAX requests with cookies inaccessible via javascript.
Change History (8)
comment:1 by , 3 years ago
Cc: | added |
---|
follow-up: 5 comment:2 by , 3 years ago
comment:3 by , 3 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
Also, this note was changed in 5d80843ebc5376d00f98bf2a6aadbada4c29365c, so the problem seems to be outdated now (see a short discussion) because it doesn't say anything about CsrfViewMiddleware
preferences. Am I right?
comment:4 by , 3 years ago
Yeah this seems about right.
I think instead of correcting the docs, the cookie first behaviour is desired.
The admin doesn't use Javascript so it has to use the token from the DOM to send along in the form submission which then is compared to the cookie server side.
However with the cookie first approach, why does one need the CSRF DOM token ({% csrftoken %})?
The CSRF token needs to be part of the forms that your browser sends. Only when you are using JS you have the option of fetching the token from somewhere else.
comment:5 by , 3 years ago
Replying to Florian Apolloner:
I honestly have no idea what the note is trying to say. The
CsrfViewMiddleware
has no access to the DOM after all :D What do I miss?
CSRF token that is POSTed, which the middleware sees, originates from a CSRF token that is embedded in the DOM (usually via a {% csrftoken %}
, as noted in the orginal post).
follow-up: 7 comment:6 by , 3 years ago
Hi Michael,
the CSRF token is POSTed but that does not mean it has to originate from the DOM. The middleware operates on the HTTP level, all it sees is post data, it has no concept of the DOM. That is why I ment I do not understand what the note is (well was, this is fixed in the dev branch) trying to say.
comment:7 by , 3 years ago
Replying to Florian Apolloner:
Hi Michael,
the CSRF token is POSTed but that does not mean it has to originate from the DOM. The middleware operates on the HTTP level, all it sees is post data, it has no concept of the DOM. That is why I ment I do not understand what the note is (well was, this is fixed in the dev branch) trying to say.
Hi Florian,
Thanks for this and the clarification of how the admin site handles it. I didn't reply to the other points because I was reading up a bit, because I think I don't understand the whole system and did not want to waste your time. I have also been reading that diff, and I see it changes the text from
The CSRF token is also present in the DOM, but only if explicitly included
usingcsrf_token
in a template. The cookie contains the canonical
token; theCsrfViewMiddleware
will prefer the cookie to the token in
the DOM. Regardless, you're guaranteed to have the cookie if the token is
present in the DOM, so you should use the cookie!
to
The CSRF token is also present in the DOM in a masked form, but only if
explicitly included usingcsrf_token
in a template. The cookie
contains the canonical, unmasked token. The
:class:~django.middleware.csrf.CsrfViewMiddleware
will accept either.
However, in order to protect againstBREACH
_ attacks, it's recommended to
use a masked token.
I am guessing the new behaviour is if the CSRF Middleware receives a request that has the X-CSRFToken
header, and a csrftoken
key in the POST data, it will prefer the POST data?
Maybe a note about how the CSRF Middleware verifies the token when : X-CSRFToken
header *and* the POST csrftoken
entry are *both* provided. This is a common scenario if one logs out and in another tab, and uses JavaScript Ajax calls that can add both:
E.g.:
- There is a POST
csrftoken
entry, the middleware tries it, and raises CSRF exception, even those there is a validX-CSRFToken
header
Or:
- There is a POST
csrftoken
entry, the middleware tries it, fails... - Check whether there is a
X-CSRFToken
header, it exists and passes, request is okay
Or.
- Tries
X-CSRFToken
header first it exists and passes, request is okay
comment:8 by , 3 years ago
I am guessing the new behaviour is if the CSRF Middleware receives a request that has the X-CSRFToken header, and a csrftoken key in the POST data, it will prefer the POST data?
Yes it will prefer POST data but I do not think this is new.
Maybe a note about how the CSRF Middleware verifies the token when : X-CSRFToken header *and* the POST csrftoken entry are *both* provided. This is a common scenario if one logs out and in another tab, and uses JavaScript Ajax calls that can add both
This should not happen imo. Normal forms always use the token in the form. Ajax should use one or the other but not both. X-CSRFToken
is mostly there if you are posting JSON and not submitting a normal form via Ajax.
Btw with at least the Django dev version, we verify the origin as well in which case the tokens are not used at all :)
I honestly have no idea what the note is trying to say. The
CsrfViewMiddleware
has no access to the DOM after all :D What do I miss?