CVE-2011-0696
Description
Django 1.1.x before 1.1.4 and 1.2.x before 1.2.5 does not properly validate HTTP requests that contain an X-Requested-With header, which makes it easier for remote attackers to conduct cross-site request forgery (CSRF) attacks via forged AJAX requests that leverage a "combination of browser plugins and redirects," a related issue to CVE-2011-0447.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | >= 1.1, < 1.1.4 | 1.1.4 |
DjangoPyPI | >= 1.2, < 1.2.5 | 1.2.5 |
Affected products
9cpe:2.3:a:djangoproject:django:1.1:*:*:*:*:*:*:*+ 8 more
- cpe:2.3:a:djangoproject:django:1.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.1.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.1.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.2.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.2.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.2.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.2.4:*:*:*:*:*:*:*
Patches
2408c5c873ce1[1.1.X] Fixed a security issue in the CSRF component. Disclosure and new release forthcoming.
3 files changed · +37 −21
django/contrib/csrf/middleware.py+6 −6 modified@@ -37,9 +37,6 @@ def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(callback, 'csrf_exempt', False): return None - if request.is_ajax(): - return None - try: session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] except KeyError: @@ -48,9 +45,12 @@ def process_view(self, request, callback, callback_args, callback_kwargs): csrf_token = _make_token(session_id) # check incoming token - try: - request_csrf_token = request.POST['csrfmiddlewaretoken'] - except KeyError: + request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') + if request_csrf_token == "": + # Fall back to X-CSRFToken, to make things easier for AJAX + request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') + + if request_csrf_token == "": return HttpResponseForbidden(_ERROR_MSG) if request_csrf_token != csrf_token:
django/contrib/csrf/tests.py+3 −3 modified@@ -135,12 +135,12 @@ def test_process_request_session_no_token_exempt_view(self): req2 = CsrfMiddleware().process_view(req, csrf_exempt(self.get_view()), (), {}) self.assertEquals(None, req2) - def test_ajax_exemption(self): + def test_csrf_token_in_header(self): """ - Check that AJAX requests are automatically exempted. + Check that we can pass in the token in a header instead of in the form """ req = self._get_POST_session_request() - req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' + req.META['HTTP_X_CSRFTOKEN'] = _make_token(self._session_id) req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {}) self.assertEquals(None, req2)
docs/ref/contrib/csrf.txt+28 −12 modified@@ -39,6 +39,34 @@ replaced instead of using ``CsrfMiddleware``. (previous versions of Django did not provide these two components of ``CsrfMiddleware`` as described above) +AJAX +---- + +While the above method can be used with AJAX POST requests, it has some +inconveniences: you have to remember to get the CSRF token from the HTML +document and pass it in as POST data with every POST request. For this reason, +there is an alternative method: on each XMLHttpRequest, set a custom +`X-CSRFToken` header to the value of the CSRF token. This is often easier, +because many javascript frameworks provide hooks that allow headers to be set on +every request. In jQuery, you can use the ``beforeSend`` hook as follows: + +.. code-block:: javascript + + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { + // Only send the token to relative URLs i.e. locally. + xhr.setRequestHeader("X-CSRFToken", + $("#csrfmiddlewaretoken").val()); + } + } + }); + +Adding this to a javascript file that is included on your site will ensure that +AJAX POST requests that are made via jQuery will not be caught by the CSRF +protection. This will only work if you remember to include a form on the page, +so that the input with id 'csrfmiddlewaretoken' will be found. + Exceptions ---------- @@ -61,10 +89,6 @@ disable the view protection mechanism (``CsrfViewMiddleware``) and the response post-processing (``CsrfResponseMiddleware``) respectively. They can be used individually if required. -You don't have to worry about doing this for most AJAX views. Any -request sent with "X-Requested-With: XMLHttpRequest" is automatically -exempt. (See the next section.) - How it works ============ @@ -98,14 +122,6 @@ The Content-Type is checked before modifying the response, and only pages that are served as 'text/html' or 'application/xml+xhtml' are modified. -The middleware tries to be smart about requests that come in via AJAX. Many -JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header; -these requests are detected and automatically *not* handled by this middleware. -We can do this safely because, in the context of a browser, the header can only -be added by using ``XMLHttpRequest``, and browsers already implement a -same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you -don't trust content within the same domain or subdomains.) - .. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
818e70344e71[1.2.X] Fixed a security issue in the CSRF componenent. Disclosure and new release forthcoming.
3 files changed · +50 −51
django/middleware/csrf.py+6 −26 modified@@ -97,6 +97,7 @@ def _reject(self, request, reason): return _get_failure_view()(request, reason=reason) def process_view(self, request, callback, callback_args, callback_kwargs): + if getattr(request, 'csrf_processing_done', False): return None @@ -130,31 +131,6 @@ def process_view(self, request, callback, callback_args, callback_kwargs): # any branches that call reject() return self._accept(request) - if request.is_ajax(): - # .is_ajax() is based on the presence of X-Requested-With. In - # the context of a browser, this can only be sent if using - # XmlHttpRequest. Browsers implement careful policies for - # XmlHttpRequest: - # - # * Normally, only same-domain requests are allowed. - # - # * Some browsers (e.g. Firefox 3.5 and later) relax this - # carefully: - # - # * if it is a 'simple' GET or POST request (which can - # include no custom headers), it is allowed to be cross - # domain. These requests will not be recognized as AJAX. - # - # * if a 'preflight' check with the server confirms that the - # server is expecting and allows the request, cross domain - # requests even with custom headers are allowed. These - # requests will be recognized as AJAX, but can only get - # through when the developer has specifically opted in to - # allowing the cross-domain POST request. - # - # So in all cases, it is safe to allow these requests through. - return self._accept(request) - if request.is_secure(): # Strict referer checking for HTTPS referer = request.META.get('HTTP_REFERER') @@ -185,7 +161,11 @@ def process_view(self, request, callback, callback_args, callback_kwargs): csrf_token = request.META["CSRF_COOKIE"] # check incoming token - request_csrf_token = request.POST.get('csrfmiddlewaretoken', None) + request_csrf_token = request.POST.get('csrfmiddlewaretoken', "") + if request_csrf_token == "": + # Fall back to X-CSRFToken, to make things easier for AJAX + request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') + if request_csrf_token != csrf_token: if cookie_is_new: # probably a problem setting the CSRF cookie
docs/ref/contrib/csrf.txt+41 −22 modified@@ -81,6 +81,47 @@ The utility script ``extras/csrf_migration_helper.py`` can help to automate the finding of code and templates that may need to be upgraded. It contains full help on how to use it. +AJAX +---- + +While the above method can be used for AJAX POST requests, it has some +inconveniences: you have to remember to pass the CSRF token in as POST data with +every POST request. For this reason, there is an alternative method: on each +XMLHttpRequest, set a custom `X-CSRFToken` header to the value of the CSRF +token. This is often easier, because many javascript frameworks provide hooks +that allow headers to be set on every request. In jQuery, you can use the +``beforeSend`` hook as follows: + +.. code-block:: javascript + + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + 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; + } + if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { + // Only send the token to relative URLs i.e. locally. + xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); + } + } + }); + +Adding this to a javascript file that is included on your site will ensure that +AJAX POST requests that are made via jQuery will not be caught by the CSRF +protection. + The decorator method -------------------- @@ -262,10 +303,6 @@ in the same module. These disable the view protection mechanism (``CsrfResponseMiddleware``) respectively. They can be used individually if required. -You don't have to worry about doing this for most AJAX views. Any request sent -with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the `How -it works`_ section.) - Subdomains ---------- @@ -343,24 +380,6 @@ request ought to be harmless. response, and only pages that are served as 'text/html' or 'application/xml+xhtml' are modified. -AJAX ----- - -The middleware tries to be smart about requests that come in via AJAX. Most -modern JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP -header; these requests are detected and automatically *not* handled by this -middleware. We can do this safely because, in the context of a browser, the -header can only be added by using ``XMLHttpRequest``, and browsers already -implement a same-domain policy for ``XMLHttpRequest``. - -For the more recent browsers that relax this same-domain policy, custom headers -like "X-Requested-With" are only allowed after the browser has done a -'preflight' check to the server to see if the cross-domain request is allowed, -using a strictly 'opt in' mechanism, so the exception for AJAX is still safe—if -the developer has specifically opted in to allowing cross-site AJAX POST -requests on a specific URL, they obviously don't want the middleware to disallow -exactly that. - .. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html Caching
tests/regressiontests/csrf_tests/tests.py+3 −3 modified@@ -275,12 +275,12 @@ def test_process_request_csrf_cookie_no_token_exempt_view(self): req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {}) self.assertEquals(None, req2) - def test_ajax_exemption(self): + def test_csrf_token_in_header(self): """ - Check that AJAX requests are automatically exempted. + Check that we can pass in the token in a header instead of in the form """ req = self._get_POST_csrf_cookie_request() - req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' + req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) self.assertEquals(None, req2)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
24- openwall.com/lists/oss-security/2011/02/09/6nvdPatchWEB
- www.djangoproject.com/weblog/2011/feb/08/security/nvdPatchVendor Advisory
- bugzilla.redhat.com/show_bug.cginvdPatchWEB
- github.com/advisories/GHSA-5j2h-h5hg-3wf8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2011-0696ghsaADVISORY
- lists.fedoraproject.org/pipermail/package-announce/2011-February/054207.htmlnvdWEB
- lists.fedoraproject.org/pipermail/package-announce/2011-February/054208.htmlnvdWEB
- secunia.com/advisories/43230nvdWEB
- secunia.com/advisories/43297nvdWEB
- secunia.com/advisories/43382nvdWEB
- secunia.com/advisories/43426nvdWEB
- www.debian.org/security/2011/dsa-2163nvdWEB
- www.djangoproject.com/weblog/2011/feb/08/securityghsaWEB
- www.mandriva.com/security/advisoriesnvdWEB
- www.securityfocus.com/bid/46296nvdWEB
- www.ubuntu.com/usn/USN-1066-1nvdWEB
- www.vupen.com/english/advisories/2011/0372nvdWEB
- www.vupen.com/english/advisories/2011/0388nvdWEB
- www.vupen.com/english/advisories/2011/0429nvdWEB
- www.vupen.com/english/advisories/2011/0439nvdWEB
- www.vupen.com/english/advisories/2011/0441nvdWEB
- github.com/django/django/commit/408c5c873ce1437c7eee9544ff279ecbad7e150aghsaWEB
- github.com/django/django/commit/818e70344e7193f6ebc73c82ed574e6ce3c91afcghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2011-10.yamlghsaWEB
News mentions
0No linked articles in our index yet.