Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#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:

  1. Log out
  2. Open two tabs to the login screen; tab A and tab B
  3. Log in on tab A, this will destroy the Anonymous session, and create a new session.
  4. Then switch to tab B and Log in
  5. 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 Mariusz Felisiak, 3 years ago

Cc: Florian Apolloner Shai Berger added

comment:2 by Florian Apolloner, 3 years ago

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?

comment:3 by Mariusz Felisiak, 3 years ago

Resolution: invalid
Status: newclosed

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 Florian Apolloner, 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.

in reply to:  2 comment:5 by Michael, 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).

comment:6 by Florian Apolloner, 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.

in reply to:  6 comment:7 by Michael, 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
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!

to

The CSRF token is also present in the DOM in a masked form, but only if
explicitly included using csrf_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 against BREACH_ 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.:

  1. There is a POST csrftoken entry, the middleware tries it, and raises CSRF exception, even those there is a valid X-CSRFToken header

Or:

  1. There is a POST csrftoken entry, the middleware tries it, fails...
  2. Check whether there is a X-CSRFToken header, it exists and passes, request is okay

Or.

  1. Tries X-CSRFToken header first it exists and passes, request is okay

comment:8 by Florian Apolloner, 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 :)

Note: See TracTickets for help on using tickets.
Back to Top