Opened 11 years ago

Closed 10 years ago

#21326 closed Bug (needsinfo)

Small bug fix for javascript example snippet in docs for CSRF in AJAX requests

Reported by: swihart.andrew@… Owned by: nobody
Component: Documentation Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

On updating my javascript AJAX CSRF token fix to match the current docs, I encountered a problem. If your browser does not yet have the CSRF cookie for your site and you did not include {% csrf_token %} somewhere in the template rendered on full page load, the following will result in csrftoken being undefined:

var csrftoken = getCookie('csrftoken');

Or with jQuery cookie plugin:

var csrftoken = $.cookie('csrftoken');

The example javascript code simply references this csrftoken variable, set on initial page load, in ajaxSetup's beforeSend method. When an AJAX form is brought in later, regardless of whether it includes the csrf cookie, the javascript code assumes it already checked for the cookie and got undefined, and it results in a 403 error.

The fix is simply to move the cookie checking function into ajaxSetup's beforeSend method. A fully working example, using Jquery >= 1.5.1 and the $.cookie plugin:

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    crossDomain: false, // obviates need for sameOrigin test
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
        }
    }
});

or a variant without the cookie plugin (see getCookie() in docs):

var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    crossDomain: false, // obviates need for sameOrigin test
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type)) {
            if (!csrftoken) { csrftoken = getCookie('csrftoken'); }
            xhr.setRequestHeader("X-CSRFToken", csrftoken));
        }
    }
});

I found I also had to use the ensure_csrf_cookie() decorator on the view used for the AJAX request in order for the token cookie to actually be included, regardless of whether I put {% csrf_token %} in the AJAX form template. I'm not sure why that is the case, but the docs imply this might be the expected behavior:


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().

This is probably more of a bug than an optimization, but since it's not really part of the Django code, I just labeled it as an optimization.

Change History (3)

comment:1 by anonymous, 11 years ago

Component: contrib.csrfDocumentation
Type: Cleanup/optimizationBug

Re-labeled as Bug and set component to Documentation.

comment:2 by Tim Graham, 11 years ago

It seems to me that your problem:

"If your browser does not yet have the CSRF cookie for your site and you did not include {% csrf_token %} somewhere in the template rendered on full page load, the following will result in csrftoken being undefined:"

is addressed by the warning you mention later:

"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()."


Does including ensure_csrf_cookie() on the first (non-AJAX) view not address the problem?

comment:3 by Tim Graham, 10 years ago

Resolution: needsinfo
Status: newclosed

Andrew, please reopen if you believe this is still an issue, thanks.

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