3

I have AJAX code which makes POST requests to a Django 1.6.4 application. The view has CSRF protection enabled via the django.middleware.csrf.CsrfViewMiddleware. If I do not pass a cookie but do pass the HTTP_X_CSRFTOKEN, it fails.

I am looking at the code of django.middleware.csrf.CsrfViewMiddleware and I see that on line 161 it checks to see if if csrf_token is None: after getting it from the cookie. If it is None, it returns. Only afterwards does it check the csrfmiddlewaretoken param and the HTTP_X_CSRFTOKEN request header. This looks incorrect and the check for a missing csrf_token value should only be made after checking all the possible places for where it could be found.

Any one else had similar issues? Am I seeing this incorrectly?

Krystian Cybulski
  • 10,789
  • 12
  • 67
  • 98

2 Answers2

7

I think the confusion might be that the CSRF cookie and the HTTP_X_CSRFTOKEN HTTP header exist on opposite sides of the comparison. In other words, to prevent CSRF attacks, Django compares:

CSRF cookie value vs. POST token value ("csrfmiddlewaretoken")

(or)

CSRF cookie value vs. HTTP header value ("HTTP_X_CSRFTOKEN")

That's why the cookie is always necessary. Using the HTTP_X_CSRFTOKEN header is a substitute for setting the token in POST data, not a substitute for the cookie.

SMX
  • 1,372
  • 15
  • 14
2

If you are using jQuery you can create a beforeSend function that includes the csrf token. Django CSRF for more information.

Please be aware that Django looks for the Header X-CSRFToken not HTTP_X_CSRFTOKEN. At least that was my problem during debugging of the code. (I also checked the django.middleware.csrf.CsrfViewMiddleware for this)


The if csrf_token is None is a extra check done by Django. (Stated from the comment in the if-statement.

No CSRF cookie. For POST requests, we insist on a CSRF cookie, and in this way we can avoid all CSRF attacks, including login CSRF.

I think (not sure) there is no single check to only validate the header from a ajax post request, and Django will do checks to prevent any form of CSRF attacks.

Community
  • 1
  • 1
Eagllus
  • 437
  • 3
  • 8
  • 1
    I am aware of the `beforeSend` and am using it. While the HTTP header is called X-CSRFToken, Django makes it available as HTTP_X_CSRFTOKEN, by prepending the HTTP_. See https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META . This, however, is not my issue. I provide the header value, and Django does not even look at it (please look at the Django source I referenced in the original post) – Krystian Cybulski Jul 17 '14 at 16:46