Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.2.X] Fixed a security issue in the CSRF componenent. Disclosure an…

…d new release forthcoming.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15465 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 818e70344e7193f6ebc73c82ed574e6ce3c91afc 1 parent fdd3bd9
Alex Gaynor authored February 09, 2011
32  django/middleware/csrf.py
@@ -97,6 +97,7 @@ def _reject(self, request, reason):
97 97
         return _get_failure_view()(request, reason=reason)
98 98
 
99 99
     def process_view(self, request, callback, callback_args, callback_kwargs):
  100
+
100 101
         if getattr(request, 'csrf_processing_done', False):
101 102
             return None
102 103
 
@@ -130,31 +131,6 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
130 131
                 # any branches that call reject()
131 132
                 return self._accept(request)
132 133
 
133  
-            if request.is_ajax():
134  
-                # .is_ajax() is based on the presence of X-Requested-With.  In
135  
-                # the context of a browser, this can only be sent if using
136  
-                # XmlHttpRequest.  Browsers implement careful policies for
137  
-                # XmlHttpRequest:
138  
-                #
139  
-                #  * Normally, only same-domain requests are allowed.
140  
-                #
141  
-                #  * Some browsers (e.g. Firefox 3.5 and later) relax this
142  
-                #    carefully:
143  
-                #
144  
-                #    * if it is a 'simple' GET or POST request (which can
145  
-                #      include no custom headers), it is allowed to be cross
146  
-                #      domain.  These requests will not be recognized as AJAX.
147  
-                #
148  
-                #    * if a 'preflight' check with the server confirms that the
149  
-                #      server is expecting and allows the request, cross domain
150  
-                #      requests even with custom headers are allowed. These
151  
-                #      requests will be recognized as AJAX, but can only get
152  
-                #      through when the developer has specifically opted in to
153  
-                #      allowing the cross-domain POST request.
154  
-                #
155  
-                # So in all cases, it is safe to allow these requests through.
156  
-                return self._accept(request)
157  
-
158 134
             if request.is_secure():
159 135
                 # Strict referer checking for HTTPS
160 136
                 referer = request.META.get('HTTP_REFERER')
@@ -185,7 +161,11 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
185 161
                 csrf_token = request.META["CSRF_COOKIE"]
186 162
 
187 163
             # check incoming token
188  
-            request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
  164
+            request_csrf_token = request.POST.get('csrfmiddlewaretoken', "")
  165
+            if request_csrf_token == "":
  166
+                # Fall back to X-CSRFToken, to make things easier for AJAX
  167
+                request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
  168
+
189 169
             if request_csrf_token != csrf_token:
190 170
                 if cookie_is_new:
191 171
                     # probably a problem setting the CSRF cookie
63  docs/ref/contrib/csrf.txt
@@ -81,6 +81,47 @@ The utility script ``extras/csrf_migration_helper.py`` can help to automate the
81 81
 finding of code and templates that may need to be upgraded.  It contains full
82 82
 help on how to use it.
83 83
 
  84
+AJAX
  85
+----
  86
+
  87
+While the above method can be used for AJAX POST requests, it has some
  88
+inconveniences: you have to remember to pass the CSRF token in as POST data with
  89
+every POST request. For this reason, there is an alternative method: on each
  90
+XMLHttpRequest, set a custom `X-CSRFToken` header to the value of the CSRF
  91
+token. This is often easier, because many javascript frameworks provide hooks
  92
+that allow headers to be set on every request. In jQuery, you can use the
  93
+``beforeSend`` hook as follows:
  94
+
  95
+.. code-block:: javascript
  96
+
  97
+    $.ajaxSetup({
  98
+        beforeSend: function(xhr, settings) {
  99
+            function getCookie(name) {
  100
+                var cookieValue = null;
  101
+                if (document.cookie && document.cookie != '') {
  102
+                    var cookies = document.cookie.split(';');
  103
+                    for (var i = 0; i < cookies.length; i++) {
  104
+                        var cookie = jQuery.trim(cookies[i]);
  105
+                        // Does this cookie string begin with the name we want?
  106
+                        if (cookie.substring(0, name.length + 1) == (name + '=')) {
  107
+                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
  108
+                            break;
  109
+                        }
  110
+                    }
  111
+                }
  112
+                return cookieValue;
  113
+            }
  114
+            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
  115
+                // Only send the token to relative URLs i.e. locally.
  116
+                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
  117
+            }
  118
+        }
  119
+    });
  120
+
  121
+Adding this to a javascript file that is included on your site will ensure that
  122
+AJAX POST requests that are made via jQuery will not be caught by the CSRF
  123
+protection.
  124
+
84 125
 The decorator method
85 126
 --------------------
86 127
 
@@ -262,10 +303,6 @@ in the same module.  These disable the view protection mechanism
262 303
 (``CsrfResponseMiddleware``) respectively.  They can be used individually if
263 304
 required.
264 305
 
265  
-You don't have to worry about doing this for most AJAX views. Any request sent
266  
-with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the `How
267  
-it works`_ section.)
268  
-
269 306
 Subdomains
270 307
 ----------
271 308
 
@@ -343,24 +380,6 @@ request ought to be harmless.
343 380
 response, and only pages that are served as 'text/html' or
344 381
 'application/xml+xhtml' are modified.
345 382
 
346  
-AJAX
347  
-----
348  
-
349  
-The middleware tries to be smart about requests that come in via AJAX. Most
350  
-modern JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP
351  
-header; these requests are detected and automatically *not* handled by this
352  
-middleware.  We can do this safely because, in the context of a browser, the
353  
-header can only be added by using ``XMLHttpRequest``, and browsers already
354  
-implement a same-domain policy for ``XMLHttpRequest``.
355  
-
356  
-For the more recent browsers that relax this same-domain policy, custom headers
357  
-like "X-Requested-With" are only allowed after the browser has done a
358  
-'preflight' check to the server to see if the cross-domain request is allowed,
359  
-using a strictly 'opt in' mechanism, so the exception for AJAX is still safe—if
360  
-the developer has specifically opted in to allowing cross-site AJAX POST
361  
-requests on a specific URL, they obviously don't want the middleware to disallow
362  
-exactly that.
363  
-
364 383
 .. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
365 384
 
366 385
 Caching
6  tests/regressiontests/csrf_tests/tests.py
@@ -275,12 +275,12 @@ def test_process_request_csrf_cookie_no_token_exempt_view(self):
275 275
         req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
276 276
         self.assertEquals(None, req2)
277 277
 
278  
-    def test_ajax_exemption(self):
  278
+    def test_csrf_token_in_header(self):
279 279
         """
280  
-        Check that AJAX requests are automatically exempted.
  280
+        Check that we can pass in the token in a header instead of in the form
281 281
         """
282 282
         req = self._get_POST_csrf_cookie_request()
283  
-        req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
  283
+        req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id
284 284
         req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
285 285
         self.assertEquals(None, req2)
286 286
 

0 notes on commit 818e703

Please sign in to comment.
Something went wrong with that request. Please try again.