Code

Ticket #16936: ajax_csrf.patch

File ajax_csrf.patch, 6.0 KB (added by idangazit, 3 years ago)

Docs patch with clearer AJAX & CSRF explanations.

  • docs/ref/contrib/csrf.txt

    diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt
    index a683947..0e1935d 100644
    a b inconveniences: you have to remember to pass the CSRF token in as POST data with 
    8686every POST request. For this reason, there is an alternative method: on each 
    8787XMLHttpRequest, set a custom `X-CSRFToken` header to the value of the CSRF 
    8888token. This is often easier, because many javascript frameworks provide hooks 
    89 that allow headers to be set on every request. In jQuery, you can use the 
    90 ``ajaxSend`` event as follows: 
     89that allow headers to be set on every request. 
     90 
     91As a first step, you must get a hold of the CSRF token itself. If you've got 
     92a form which was rendered with the :ttag:`csrf_token`, then you can extract the 
     93token from there: 
     94 
     95.. code-block:: javascript 
     96 
     97    // jQuery 
     98    var csrftoken = $("input[name=csrfmiddlewaretoken]").val(); 
     99 
     100If the CSRF token is not present in markup by use of the :ttag:`csrf_token` 
     101template tag, it must be supplied to the client by other means. This is common 
     102in cases where the form is dynamically added to the page, and Django supplies 
     103a decorator which will set a cookie containing the CSRF token: 
     104:func:`~django.views.decorators.csrf.ensure_csrf_cookie`. Use the decorator 
     105on any view which will need access to the CSRF token, but doesn't include the 
     106token in the actual markup sent to the client. 
    91107 
    92108.. code-block:: javascript 
    93109 
    94     $(document).ajaxSend(function(event, xhr, settings) { 
    95         function getCookie(name) { 
    96             var cookieValue = null; 
    97             if (document.cookie && document.cookie != '') { 
    98                 var cookies = document.cookie.split(';'); 
    99                 for (var i = 0; i < cookies.length; i++) { 
    100                     var cookie = jQuery.trim(cookies[i]); 
    101                     // Does this cookie string begin with the name we want? 
    102                     if (cookie.substring(0, name.length + 1) == (name + '=')) { 
    103                         cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 
    104                         break; 
    105                     } 
     110    // once again, using jQuery 
     111    function getCookie(name) { 
     112        var cookieValue = null; 
     113        if (document.cookie && document.cookie != '') { 
     114            var cookies = document.cookie.split(';'); 
     115            for (var i = 0; i < cookies.length; i++) { 
     116                var cookie = jQuery.trim(cookies[i]); 
     117                // Does this cookie string begin with the name we want? 
     118                if (cookie.substring(0, name.length + 1) == (name + '=')) { 
     119                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 
     120                    break; 
    106121                } 
    107122            } 
    108             return cookieValue; 
    109         } 
    110         function sameOrigin(url) { 
    111             // url could be relative or scheme relative or absolute 
    112             var host = document.location.host; // host + port 
    113             var protocol = document.location.protocol; 
    114             var sr_origin = '//' + host; 
    115             var origin = protocol + sr_origin; 
    116             // Allow absolute or scheme relative URLs to same origin 
    117             return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || 
    118                 (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || 
    119                 // or any other URL that isn't scheme relative or absolute i.e relative. 
    120                 !(/^(\/\/|http:|https:).*/.test(url)); 
    121         } 
    122         function safeMethod(method) { 
    123             return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 
    124123        } 
     124        return cookieValue; 
     125    } 
     126    var csrftoken = getCookie('csrftoken'); 
    125127 
    126         if (!safeMethod(settings.type) && sameOrigin(settings.url)) { 
    127             xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); 
    128         } 
    129     }); 
     128The above code could be simplified by using the `jQuery cookie plugin 
     129<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``: 
    130130 
    131 .. note:: 
     131.. code-block:: javascript 
    132132 
    133     Due to a bug introduced in jQuery 1.5, the example above will not work 
    134     correctly on that version. Make sure you are running at least jQuery 1.5.1. 
     133    var csrftoken = $.cookie('csrftoken'); 
    135134 
    136 Adding this to a javascript file that is included on your site will ensure that 
    137 AJAX POST requests that are made via jQuery will not be caught by the CSRF 
    138 protection. 
     135Finally, you'll have to actually set the header on your AJAX request, while 
     136protecting the CSRF token from being sent to other domains: 
    139137 
    140 The above code could be simplified by using the `jQuery cookie plugin 
    141 <http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``, and 
    142 `settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in jQuery 1.5 and 
    143 later to replace ``sameOrigin``. 
     138.. code-block:: javascript 
     139 
     140    function safeMethod(method) { 
     141        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 
     142    } 
     143    $.ajaxSetup({ 
     144        beforeSend: function(xhr, settings) { 
     145            if (!(/^https?:.*/.test(settings.url)) && safeMethod(settings.type) ) { 
     146                // Only send the token to relative URLs i.e. locally. 
     147                // Only send the token when using a safe HTTP method. 
     148                // Using the csrftoken value acquired earlier. 
     149                xhr.setRequestHeader("X-CSRFToken", csrftoken); 
     150            } 
     151        } 
     152    }); 
     153 
     154You can use `settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in 
     155jQuery 1.5 and newer in order to simplify the above code: 
    144156 
    145 In addition, if the CSRF cookie has not been sent to the client by use of 
    146 :ttag:`csrf_token`, you may need to ensure the client receives the cookie by 
    147 using :func:`~django.views.decorators.csrf.ensure_csrf_cookie`. 
     157.. code-block:: javascript 
     158 
     159    $.ajaxSetup({ 
     160        crossDomain: false, 
     161        beforeSend: function(xhr, settings) { 
     162            if (safeMethod(settings.type) { 
     163                xhr.setRequestHeader("X-CSRFToken", csrftoken); 
     164            } 
     165        } 
     166    }); 
    148167 
    149168The decorator method 
    150169--------------------