Skip to content

Commit 408c5c8

Browse files
committed
[1.1.X] Fixed a security issue in the CSRF component. Disclosure and new release forthcoming.
git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@15466 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 274bd67 commit 408c5c8

File tree

3 files changed

+37
-21
lines changed

3 files changed

+37
-21
lines changed

django/contrib/csrf/middleware.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
3737
if getattr(callback, 'csrf_exempt', False):
3838
return None
3939

40-
if request.is_ajax():
41-
return None
42-
4340
try:
4441
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
4542
except KeyError:
@@ -48,9 +45,12 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
4845

4946
csrf_token = _make_token(session_id)
5047
# check incoming token
51-
try:
52-
request_csrf_token = request.POST['csrfmiddlewaretoken']
53-
except KeyError:
48+
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
49+
if request_csrf_token == "":
50+
# Fall back to X-CSRFToken, to make things easier for AJAX
51+
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
52+
53+
if request_csrf_token == "":
5454
return HttpResponseForbidden(_ERROR_MSG)
5555

5656
if request_csrf_token != csrf_token:

django/contrib/csrf/tests.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,12 @@ def test_process_request_session_no_token_exempt_view(self):
135135
req2 = CsrfMiddleware().process_view(req, csrf_exempt(self.get_view()), (), {})
136136
self.assertEquals(None, req2)
137137

138-
def test_ajax_exemption(self):
138+
def test_csrf_token_in_header(self):
139139
"""
140-
Check that AJAX requests are automatically exempted.
140+
Check that we can pass in the token in a header instead of in the form
141141
"""
142142
req = self._get_POST_session_request()
143-
req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
143+
req.META['HTTP_X_CSRFTOKEN'] = _make_token(self._session_id)
144144
req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
145145
self.assertEquals(None, req2)
146146

docs/ref/contrib/csrf.txt

+28-12
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,34 @@ replaced instead of using ``CsrfMiddleware``.
3939
(previous versions of Django did not provide these two components
4040
of ``CsrfMiddleware`` as described above)
4141

42+
AJAX
43+
----
44+
45+
While the above method can be used with AJAX POST requests, it has some
46+
inconveniences: you have to remember to get the CSRF token from the HTML
47+
document and pass it in as POST data with every POST request. For this reason,
48+
there is an alternative method: on each XMLHttpRequest, set a custom
49+
`X-CSRFToken` header to the value of the CSRF token. This is often easier,
50+
because many javascript frameworks provide hooks that allow headers to be set on
51+
every request. In jQuery, you can use the ``beforeSend`` hook as follows:
52+
53+
.. code-block:: javascript
54+
55+
$.ajaxSetup({
56+
beforeSend: function(xhr, settings) {
57+
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
58+
// Only send the token to relative URLs i.e. locally.
59+
xhr.setRequestHeader("X-CSRFToken",
60+
$("#csrfmiddlewaretoken").val());
61+
}
62+
}
63+
});
64+
65+
Adding this to a javascript file that is included on your site will ensure that
66+
AJAX POST requests that are made via jQuery will not be caught by the CSRF
67+
protection. This will only work if you remember to include a form on the page,
68+
so that the input with id 'csrfmiddlewaretoken' will be found.
69+
4270
Exceptions
4371
----------
4472

@@ -61,10 +89,6 @@ disable the view protection mechanism (``CsrfViewMiddleware``) and the
6189
response post-processing (``CsrfResponseMiddleware``) respectively.
6290
They can be used individually if required.
6391

64-
You don't have to worry about doing this for most AJAX views. Any
65-
request sent with "X-Requested-With: XMLHttpRequest" is automatically
66-
exempt. (See the next section.)
67-
6892
How it works
6993
============
7094

@@ -98,14 +122,6 @@ The Content-Type is checked before modifying the response, and only
98122
pages that are served as 'text/html' or 'application/xml+xhtml'
99123
are modified.
100124

101-
The middleware tries to be smart about requests that come in via AJAX. Many
102-
JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header;
103-
these requests are detected and automatically *not* handled by this middleware.
104-
We can do this safely because, in the context of a browser, the header can only
105-
be added by using ``XMLHttpRequest``, and browsers already implement a
106-
same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you
107-
don't trust content within the same domain or subdomains.)
108-
109125

110126
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
111127

0 commit comments

Comments
 (0)