2
votes

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.

2
Did you try to send X-CSRFToken header?Ernest Ten
Also, what getCookie('csrftoken') call returns? I assume that CSRF_COOKIE_NAME has it's default value.Ernest Ten
On Linux it returns AjQzJy3tRZ2awslgdibkDTvQgANFQKmP, on Windows it returns null...Neurion

2 Answers

7
votes

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.

0
votes

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.