"CSRF token missing or incorrect" while post parameter via AJAX in Django

I try to post parameter like

 jQuery.ajax(
        {
            'type': 'POST',
            'url': url,
            'contentType': 'application/json',
            'data': "{content:'xxx'}",
            'dataType': 'json',
            'success': rateReviewResult 
        }
    );

However, Django return Forbidden 403. CSRF verification failed. Request aborted. I am using 'django.middleware.csrf.CsrfViewMiddleware' and couldn't find how I can prevent this problem without compromising security.


Solution 1:

You can make AJAX post request in two different ways:

  1. To tell your view not to check the csrf token. This can be done by using decorator @csrf_exempt, like this:

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def your_view_name(request):
        ...
    
  2. To embed a csrf token in each AJAX request, for jQuery it may be:

    $(function () {
        $.ajaxSetup({
            headers: { "X-CSRFToken": getCookie("csrftoken") }
        });
    });
    

    Where the getCookie function retrieves csrf token from cookies. I use the following implementation:

    function getCookie(c_name)
    {
        if (document.cookie.length > 0)
        {
            c_start = document.cookie.indexOf(c_name + "=");
            if (c_start != -1)
            {
                c_start = c_start + c_name.length + 1;
                c_end = document.cookie.indexOf(";", c_start);
                if (c_end == -1) c_end = document.cookie.length;
                return unescape(document.cookie.substring(c_start,c_end));
            }
        }
        return "";
     }
    

    Also, jQuery has a plugin for accessing cookies, something like that:

    // set cookie
    $.cookie('cookiename', 'cookievalue');
    // read cookie
    var myCookie = $.cookie('cookiename');
    // delete cookie
    $.cookie('cookiename', null);
    

Solution 2:

The simplest way I have found is to include the {{csrf_token}} value in the data:

jQuery.ajax(
    {
        'type': 'POST',
        'url': url,
        'contentType': 'application/json',
        'data': {
            'content': 'xxx',
            'csrfmiddlewaretoken': '{{ csrf_token }}',
        },
        'dataType': 'json',
        'success': rateReviewResult 
    }
);

Solution 3:

It took me a while to understand what to do with the code that Daniel posted. But actually all you have to do is paste it at the beginning of the javascript file.

For me, the best solution so far is:

  1. Create a csrf.js file

  2. Paste the code in the csrf.js file

  3. Reference the code in the template you need it

    <script type="text/javascript" src="{{ STATIC_PREFIX }}js/csrf.js"></script>
    

Notice that STATIC_PREFIX/js/csrf.js points to my file. I am actually loading the STATIC_PREFIX variable with {% get_static_prefix as STATIC_PREFIX %}.


Advanced tip: if you are using templates and have something like base.html where you extend from, then you can just reference the script from there and you don't have to worry anymore in there rest of your files. As far as I understand, this shouldn't represent any security issue either.

Solution 4:

Simple and short

$.ajaxSetup({
  headers: { "X-CSRFToken": '{{csrf_token}}' }
});

OR

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", '{{csrf_token}}');
    }
  }
});

docs

Solution 5:

For lack of a straight forward answer, you just have to add the header X-CSRFToken to the ajax request which is in the cookie csrftoken. JQuery doesn't do cookies (for some reason) without a plugin so:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>

and the minimal code change is:

$.ajax({
  headers: { "X-CSRFToken": $.cookie("csrftoken") },
  ...
});