85

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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
brsbilgic
  • 11,613
  • 16
  • 64
  • 94

14 Answers14

122

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);
    
Michal Šrůtek
  • 1,647
  • 16
  • 17
sigurd
  • 3,071
  • 4
  • 29
  • 37
59

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 
    }
);
Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
jerrykan
  • 1,076
  • 9
  • 12
  • 5
    What if your JavaScript isn't processed by Django? Guess you'd really be up the creek. – Naftuli Kay Sep 18 '11 at 04:21
  • 4
    The original question stated that they were using 'django.middleware.csrf.CsrfViewMiddleware' and Django was returning the error, so I think it is pretty safe to assume that Django is processing the ajax request. – jerrykan Sep 20 '11 at 05:48
  • 7
    The problem is that Django isn't templatizing the JS, only the HTML views. – Naftuli Kay Sep 20 '11 at 16:54
  • 4
    then do this in the base.html `window.csrftoken="{{csrftoken}}";` – airtonix Jun 22 '13 at 09:19
  • I get a jquery.js:8423 Uncaught RangeError: Maximum call stack size exceeded when I try that ! – Laurent Dec 06 '17 at 14:32
  • 2
    he can add a csrf token input in html and use jquery to get that token if js is not processed by django. add `{{ csrf_token }}` in the form and get the value by `csrf_token = $('input[name="csrfmiddlewaretoken"]').val();` and pass it along with data `data = {'para1': 'para1_value', csrfmiddlewaretoken: csrf_token};` – vikas devde Aug 05 '18 at 17:13
26

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.

toto_tico
  • 17,977
  • 9
  • 97
  • 116
  • 1
    By `the code` you mean exactly every single character inside the green background? I copy paste that and did as you told, but still get 403 forbidden error. Maybe things have changed? – Philip007 May 26 '13 at 17:34
  • @Philip007, yes, the green background. They changed the doc for [Django 1.5](https://docs.djangoproject.com/en/1.5/ref/contrib/csrf/), however I don't see any real difference in the resulting codes. They are just giving a longer explanation and the option of using jQuery. – toto_tico May 27 '13 at 09:31
  • @Phillip007, are u sure you are pointing to the correct js file `src="{{ STATIC_PREFIX }}js/csrft.js"`. Consider that STATIC_PREFIX is a variable. I set this variable with `{% get_static_prefix as STATIC_PREFIX %}`. However make sure that the `src` is pointing to the right place. – toto_tico May 27 '13 at 09:33
  • @Philip007, I just realized that there was a mistake in `src` line. It said `csrft.js` instead of `csrf.js`. It would be fun if that is the mistake considering that this answer already got some upvotes :P. – toto_tico May 27 '13 at 09:41
  • 1
    Haha, thanks. I noticed that from beginning. Not culprit in my case:) I solved the problem by using jQuery plugin "jQuery-cookie". It's so much easier for me to undertand. – Philip007 May 27 '13 at 09:44
  • @Phillip007, great! feel free to edit my answer, if you are still using the csrf.js. I guess it is better to paste the whole code in [pastebin](http://pastebin.com) since de Django 1.5 change the way of doing it. – toto_tico May 28 '13 at 03:16
  • This is not canonical as in "beer". This is bypassing csrf_token. Advanced tip of toto_tico is advance as in "bypass". DO think your all js should be handled by a static server. If there must be exceptions you should control them by getting the token from your very own cookie as in sigrud's answer or by csrf_exempt decorator for this particular view – binboavetonik Sep 16 '17 at 01:25
21

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

Khaino
  • 3,774
  • 1
  • 27
  • 36
9

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") },
  ...
});
CpILL
  • 6,169
  • 5
  • 38
  • 37
6

The fastest solution without any plugins if you are not embedding js into your template is:

Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> before your reference to script.js file in your template, then add csrfmiddlewaretoken into your data dictionary:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

If you do embed your js into the template, it's as simple as: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

Marek Židek
  • 809
  • 1
  • 15
  • 31
4

Thank you everyone for all the answers. I am using Django 1.5.1. I'm a little late to the party, but here goes.

I found the link to the Django project to be very useful, but I didn't really want to have to include the extra JavaScript code every time I wanted to make an Ajax call.

I like jerrykan's response as it is very succinct and only adds one line to an otherwise normal Ajax call. In response to the comments below his comment regarding situations when Django template tags are unavailable, how about loading up the csrfmiddlewaretoken from the DOM?

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    data: { 'csrfmiddlewaretoken': token },
    dataType: 'json',
    success: function(data) { console.log('Yippee! ' + data); } 
});

EDIT March 2016

My approach to this issue over the past few years has changed. I add the code below (from the Django docs) to a main.js file and load it on every page. Once done, you shouldn't need to worry about the CSRF token with ajax again.

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');
jbiz
  • 394
  • 1
  • 5
4

I got the same issue yesterday and thought it would help people if there were a simple way to handle it, so I wrote a jQuery plugin for that: jquery.djangocsrf. Instead of adding the CSRF token in every request, it hooks itself on the AjaxSend jQuery event and adds the client cookie in a header.

Here’s how to use it:

1- include it:

<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.cookie.js"></script>
<script src="path/to/jquery.djangocsrf.js"></script>

2- enable it in your code:

$.djangocsrf( "enable" );

Django always add the token in a cookie if your template uses {% csrf_token %}. To ensure it always adds it even if you don’t use the special tag in your template, use the @ensure_csrf_cookie decorator:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def my_view(request):
    return render(request, 'mytemplate.html')

Note: I’m using Django 1.6.2.

bfontaine
  • 18,169
  • 13
  • 73
  • 107
4

Include x-csrftoken header in request:

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    beforeSend : function(jqXHR, settings) {
        jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie());
    },
    data: data,
    dataType: 'json',

});
Hasan Ramezani
  • 5,004
  • 24
  • 30
0

If, after reading other answers, someone is still struggling please try this:

   $.ajax({
            type: "POST",
            beforeSend: function (request)
            {
                request.setRequestHeader("X-CSRF-TOKEN", "${_csrf.token}");
            },
            url: servlet_path,
            data : data,
            success : function(result) {
            console.log("Success!");
   }
});
Fr333du
  • 191
  • 13
  • I was not able to get this to work as described here. Any ideas? It appears as the `beforeSend` property isn't grabbing the token properly...? – twknab May 07 '17 at 04:09
  • If anyone's still wondering, it's X-CSRFTOKEN, not X-CSRF-TOKEN. Mind the hyphens. – Endre Both Jan 29 '19 at 07:35
0

Please not that when doing it this way make sure you don't have the {% csrf_token %} inside the <form></form> tags. Then as explained here add the following code to your javascript

    function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
    }
    const csrftoken = getCookie('csrftoken');

// using js fetch
// https://docs.djangoproject.com/en/3.1/ref/csrf/#setting-the-token-on-the-ajax-request
    const request = new Request(
    /* URL */,
    {headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
    method: 'POST',
    mode: 'same-origin'  // Do not send CSRF token to another domain.
}).then(function(response) {
    // ...
});
stue
  • 81
  • 2
0

Just want to put it out here that if GET works in your use case, then it wouldn't need the CSRF token. For my use case, using GET was OK.

Anupam
  • 14,950
  • 19
  • 67
  • 94
0

html

<form action="">
        {% csrf_token %}
    </form>

JS

 <script>   
     const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
              const request = new Request(
                   'url_here',
                  {headers: {'X-CSRFToken': csrftoken}}
              );
              fetch(request, {
                  method: 'POST',
                  // mode: 'same-origin' optinal // Do not send CSRF token to another domain.
              }).then(function(response) {
                  console.log(response);
              });
    
    </script>

reference link for more detail

azhar
  • 351
  • 3
  • 13
0

As a summary for my mistakes:

  1. Don't forget to set the request content type.
  2. Get the csrf value, either from

    NB. The default cookie name is csrftoken, but can be overriden by CSRF_COOKIE_NAME setting.

    • The DOM, if you can't access the cookie (you set CSRF_USE_SESSIONS or CSRF_COOKIE_HTTPONLY to True)

    document.querySelector('[name=csrfmiddlewaretoken]').value;

  3. Set the request header, I'am using XMLHttpRequest
const Http = new XMLHttpRequest();
Http.setRequestHeader("X-CSRFToken", CSRF_VALUE);
Http.setRequestHeader("X_CSRFTOKEN", CSRF_VALUE);

The header name is managed by CSRF_HEADER_NAME setting, which its default is HTTP_X_CSRFTOKEN.

But: "The header name received from the server is normalized by converting all characters to uppercase, replacing any hyphens with underscores, and adding an 'HTTP_' prefix to the name" src.

So, If you set the HTTP_X_CSRFTOKEN header, Django will convert it to HTTP_HTTP_X_CSRFTOKEN which wis incorrect name and will raise CSRF missed error.

Http.setRequestHeader("X-CSRFToken", csrftoken);        // This worked
Http.setRequestHeader("X-CSRFTOKEN", csrftoken);        // Also this
Http.setRequestHeader("HTTP-X-CSRFToken", csrftoken);   // Not working
Http.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken);   // Not Working
Http.setRequestHeader("X_CSRFTOKEN", csrftoken);        // Not working !!
  1. Don't use url in ajax that is different than that of the browser 127.0.0.1 & localhost are not the same !!

  2. No need to set data.append("csrfmiddlewaretoken", csrftoken); in the request body, As I know.

tabebqena
  • 1,204
  • 1
  • 13
  • 23