2

I developed my application on Linux and the AJAX requests work fine. I have pulled the application to a Windows machine but the AJAX requests fail, I just get a 403 Forbidden error. From looking online, I think it is a problem with the csrf token. In Linux, I can see csrftoken:"AjQzJy3tRZ2awslgdibkDTvQgANFQKmP" under Cookies of the AJAX requests. I don't see any cookies set in Windows.

This is the Javascript code I use to get the csrf cookie. It is from https://docs.djangoproject.com/en/1.8/ref/csrf/

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;
}

This is where I submit the AJAX request:

function refreshInformation(){
$.ajax({
    type: "POST",
    url: "get_flows_info",
    data: {
           csrfmiddlewaretoken: getCookie('csrftoken')
    }
    dataType : "json",
    async : true,
    error : function(data){
        alert('AJAX error:' + data);
    },
    success : function(json_data){
            // do stuff...
    },
}); 
}

This is the view being requested:

def get_flows_info(request):
    if request.is_ajax():

          # do stuff...

        return HttpResponse(json.dumps(ret), content_type='application/json')

I found this: Django CSRF check failing with an Ajax POST request but the jQuery doesn't make any difference.

Any help?

Thanks.

Community
  • 1
  • 1
Neurion
  • 379
  • 6
  • 15
  • Did you try to send `X-CSRFToken` header? – Ernest Jul 14 '15 at 11:25
  • Also, what `getCookie('csrftoken')` call returns? I assume that [`CSRF_COOKIE_NAME`](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-CSRF_COOKIE_NAME) has it's default value. – Ernest Jul 14 '15 at 11:26
  • On Linux it returns `AjQzJy3tRZ2awslgdibkDTvQgANFQKmP`, on Windows it returns null... – Neurion Jul 14 '15 at 11:38

2 Answers2

7

Here is what can be done:

  1. Check CSRF token cookie name.

    See CSRF_COOKIE_NAME for more information.

  2. Add ensure_csrf_cookie decorator to your view (the one that renders page).

    According to the docs:

    Warning

    If your view is not rendering a template containing the csrf_token template tag, Django might not set the CSRF token cookie. This is common in cases where forms are dynamically added to the page. To address this case, Django provides a view decorator which forces setting of the cookie: ensure_csrf_cookie().

  3. Assuming that CSRF token cookie name is csrftoken, try to send X-CSRFToken header.

    $.ajax({
        // Your options here.
        headers: {'X-CSRFToken': getCookie('csrftoken')}
    });
    

Read Cross Site Request Forgery protection for more information.

Ernest
  • 2,799
  • 12
  • 28
  • Thanks for replying, but getCookie returns null so that didn't work. – Neurion Jul 14 '15 at 11:44
  • @BelegNeurion Check CSRF token cookie name by printing [`CSRF_COOKIE_NAME`](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-CSRF_COOKIE_NAME) from settings. – Ernest Jul 14 '15 at 11:47
  • Where should I print it? I tried `print(CSRF_COOKIE_NAME)` where I call my index view but it says it is not defined. – Neurion Jul 14 '15 at 12:00
  • @BelegNeurion Print it in any view and then visit it. First, import settings (`from django.conf import settings`) and then call `print settings.CSRF_COOKIE_NAME`. – Ernest Jul 14 '15 at 12:04
  • @BelegNeurion Nice. Can you execute `document.cookie.indexOf('csrftoken')` in your browser's console? – Ernest Jul 14 '15 at 12:14
  • It returns -1. I should also mention that cookies ARE enabled on my browser, and I have tried both Chrome and Firefox. – Neurion Jul 14 '15 at 12:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83204/discussion-between-ernest-ten-and-belegneurion). – Ernest Jul 14 '15 at 12:17
  • Yep it turns out the cookie wasn't set. It worked on my Linux machine because the cookie was already set from when I initially set it up. I must have removed the cookie initialisation somewhere along the line. I just added `ensure_csrf_cookie` before my view. – Neurion Jul 14 '15 at 12:58
0

What I did is just what the Doc suggests: add a header to all the Ajax request, instead of POSTing the csrftoken in every ajax request. It works for me(only tested in Linux)

The first part is to get the cookie as you have done.

<script type="text/javascript">
    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;
    }
    var csrftoken = getCookie('csrftoken');
....

Then, as the doc suggests, use this method:

function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

$.ajaxSetup({
   beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

At last, do your ajax call as usual, without adding the csrftoken as additional param.

It is useful when you want to do multiple ajax call in the same page. It is also recommended. And, if previously you don't have CSRF protection and now you have to add it, it does not involve changing existent code.

Repeat these lines in every page where ajax is used, instead of repeating the token in every ajax call.

WesternGun
  • 11,303
  • 6
  • 88
  • 157