The Python Oracle

Django CSRF check failing with an Ajax POST request

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Hypnotic Puzzle3

--

Chapters
00:00 Django Csrf Check Failing With An Ajax Post Request
01:05 Answer 1 Score 207
01:20 Accepted Answer Score 195
03:01 Answer 3 Score 79
03:13 Answer 4 Score 20
04:01 Answer 5 Score 19
04:28 Thank you

--

Full question
https://stackoverflow.com/questions/5100...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #ajax #django #csrf

#avk47



ANSWER 1

Score 207


If you use the $.ajax function, you can simply add the csrf token in the data body:

$.ajax({
    data: {
        somedata: 'somedata',
        moredata: 'moredata',
        csrfmiddlewaretoken: '{{ csrf_token }}'
    },



ACCEPTED ANSWER

Score 195


Real solution

Ok, I managed to trace the problem down. It lies in the Javascript (as I suggested below) code.

What you need is this:

$.ajaxSetup({ 
     beforeSend: function(xhr, settings) {
         function getCookie(name) {
             var cookieValue = null;
             if (document.cookie && document.cookie != '') {
                 var cookies = document.cookie.split(';');
                 for (var i = 0; i < cookies.length; i++) {
                     var cookie = jQuery.trim(cookies[i]);
                     // Does this cookie string begin with the name we want?
                     if (cookie.substring(0, name.length + 1) == (name + '=')) {
                         cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                         break;
                     }
                 }
             }
             return cookieValue;
         }
         if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
             // Only send the token to relative URLs i.e. locally.
             xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
         }
     } 
});

instead of the code posted in the official docs: https://docs.djangoproject.com/en/2.2/ref/csrf/

The working code, comes from this Django entry: http://www.djangoproject.com/weblog/2011/feb/08/security/

So the general solution is: "use ajaxSetup handler instead of ajaxSend handler". I don't know why it works. But it works for me :)

Previous post (without answer)

I'm experiencing the same problem actually.

It occurs after updating to Django 1.2.5 - there were no errors with AJAX POST requests in Django 1.2.4 (AJAX wasn't protected in any way, but it worked just fine).

Just like OP, I have tried the JavaScript snippet posted in Django documentation. I'm using jQuery 1.5. I'm also using the "django.middleware.csrf.CsrfViewMiddleware" middleware.

I tried to follow the the middleware code and I know that it fails on this:

request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')

and then

if request_csrf_token != csrf_token:
    return self._reject(request, REASON_BAD_TOKEN)

this "if" is true, because "request_csrf_token" is empty.

Basically it means that the header is NOT set. So is there anything wrong with this JS line:

xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));

?

I hope that provided details will help us in resolving the issue :)




ANSWER 3

Score 79


Add this line to your jQuery code:

$.ajaxSetup({
  data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

and done.




ANSWER 4

Score 20


The issue is because django is expecting the value from the cookie to be passed back as part of the form data. The code from the previous answer is getting javascript to hunt out the cookie value and put it into the form data. Thats a lovely way of doing it from a technical point of view, but it does look a bit verbose.

In the past, I have done it more simply by getting the javascript to put the token value into the post data.

If you use {% csrf_token %} in your template, you will get a hidden form field emitted that carries the value. But, if you use {{ csrf_token }} you will just get the bare value of the token, so you can use this in javascript like this....

csrf_token = "{{ csrf_token }}";

Then you can include that, with the required key name in the hash you then submit as the data to the ajax call.