Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #15258 - Ajax CSRF protection doesn't apply to PUT or DELETE re…

…quests

Thanks to brodie for the report, and further input from tow21

This is a potentially backwards incompatible change - if you were doing
PUT/DELETE requests and relying on the lack of protection, you will need to
update your code, as noted in the releaste notes.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16201 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit cb060f0f340356ac71ed7db5399753edce278766 1 parent 8cbcf1d
@spookylukey spookylukey authored
View
13 django/middleware/csrf.py
@@ -107,7 +107,8 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(callback, 'csrf_exempt', False):
return None
- if request.method == 'POST':
+ # Assume that anything not defined as 'safe' by RC2616 needs protection.
+ if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
# Mechanism to turn off CSRF checks for test suite. It comes after
# the creation of CSRF cookies, so that everything else continues to
@@ -165,10 +166,14 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
)
return self._reject(request, REASON_NO_CSRF_COOKIE)
- # check incoming token
- request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
+ # check non-cookie token for match
+ request_csrf_token = ""
+ if request.method == "POST":
+ request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
+
if request_csrf_token == "":
- # Fall back to X-CSRFToken, to make things easier for AJAX
+ # Fall back to X-CSRFToken, to make things easier for AJAX,
+ # and possible for PUT/DELETE
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
if not constant_time_compare(request_csrf_token, csrf_token):
View
25 docs/ref/contrib/csrf.txt
@@ -13,11 +13,13 @@ who visits the malicious site in their browser. A related type of attack,
'login CSRF', where an attacking site tricks a user's browser into logging into
a site with someone else's credentials, is also covered.
-The first defense against CSRF attacks is to ensure that GET requests are
-side-effect free. POST requests can then be protected by following the steps
-below.
+The first defense against CSRF attacks is to ensure that GET requests (and other
+'safe' methods, as defined by `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_) are
+side-effect free. Requests via 'unsafe' methods, such as POST, PUT and DELETE,
+can then be protected by following the steps below.
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
+.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
How to use it
=============
@@ -198,9 +200,9 @@ The CSRF protection is based on the following things:
This part is done by the template tag.
-3. For all incoming POST requests, a CSRF cookie must be present, and the
- 'csrfmiddlewaretoken' field must be present and correct. If it isn't, the
- user will get a 403 error.
+3. For all incoming requests that are not using HTTP GET, HEAD, OPTIONS or
+ TRACE, a CSRF cookie must be present, and the 'csrfmiddlewaretoken' field
+ must be present and correct. If it isn't, the user will get a 403 error.
This check is done by ``CsrfViewMiddleware``.
@@ -215,12 +217,11 @@ The CSRF protection is based on the following things:
This ensures that only forms that have originated from your Web site can be used
to POST data back.
-It deliberately only targets HTTP POST requests (and the corresponding POST
-forms). GET requests ought never to have any potentially dangerous side effects
-(see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET
-request ought to be harmless.
-
-.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+It deliberately ignores GET requests (and other requests that are defined as
+'safe' by RFC 2616). These requests ought never to have any potentially
+dangerous side effects , and so a CSRF attack with a GET request ought to be
+harmless. RFC 2616 defines POST, PUT and DELETE as 'unsafe', and all other
+methods are assumed to be unsafe, for maximum protection.
Caching
=======
View
12 docs/releases/1.4.txt
@@ -214,3 +214,15 @@ you should add the following lines in your settings file::
Don't forget to escape characters that have a special meaning in a regular
expression.
+
+CSRF protection extended to PUT and DELETE
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Previously, Django's :doc:`CSRF protection </ref/contrib/csrf/>` provided
+protection against only POST requests. Since use of PUT and DELETE methods in
+AJAX applications is becoming more common, we now protect all methods not
+defined as safe by RFC 2616 i.e. we exempt GET, HEAD, OPTIONS and TRACE, and
+enforce protection on everything.
+
+If you using PUT or DELETE methods in AJAX applications, please see the
+:ref:`instructions about using AJAX and CSRF <csrf-ajax>`.
View
31 tests/regressiontests/csrf_tests/tests.py
@@ -164,6 +164,37 @@ def test_csrf_token_in_header(self):
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
self.assertEqual(None, req2)
+ def test_put_and_delete_rejected(self):
+ """
+ Tests that HTTP PUT and DELETE methods have protection
+ """
+ req = TestingHttpRequest()
+ req.method = 'PUT'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(403, req2.status_code)
+
+ req = TestingHttpRequest()
+ req.method = 'DELETE'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(403, req2.status_code)
+
+ def test_put_and_delete_allowed(self):
+ """
+ Tests that HTTP PUT and DELETE methods can get through with
+ X-CSRFToken and a cookie
+ """
+ req = self._get_GET_csrf_cookie_request()
+ req.method = 'PUT'
+ req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(None, req2)
+
+ req = self._get_GET_csrf_cookie_request()
+ req.method = 'DELETE'
+ req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(None, req2)
+
# Tests for the template tag method
def test_token_node_no_csrf_cookie(self):
"""
Please sign in to comment.
Something went wrong with that request. Please try again.