Ticket #16010: crsf-origin-check.diff
File crsf-origin-check.diff, 7.4 KB (added by , 14 years ago) |
---|
-
django/middleware/csrf.py
From 063990cabc795bd8989814c6e6e03fc148e23509 Mon Sep 17 00:00:00 2001 From: David Benjamin <davidben@mit.edu> Date: Sat, 7 May 2011 23:21:07 -0400 Subject: [PATCH] Added support for the Origin header in CSRF middleware Also added documentation and unit tests for it. --- django/middleware/csrf.py | 29 +++++++++++-- docs/ref/contrib/csrf.txt | 6 +++ tests/regressiontests/csrf_tests/tests.py | 63 ++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 2f36f18..b412406 100644
a b else: 25 25 randrange = random.randrange 26 26 _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63 27 27 28 REASON_BAD_ORIGIN = "Origin checking failed - %s does not match %s." 28 29 REASON_NO_REFERER = "Referer checking failed - no Referer." 29 30 REASON_BAD_REFERER = "Referer checking failed - %s does not match %s." 30 31 REASON_NO_CSRF_COOKIE = "CSRF cookie not set." … … class CsrfViewMiddleware(object): 115 116 # any branches that call reject() 116 117 return self._accept(request) 117 118 119 # Note that request.get_host() includes the port 120 good_origin = '%s://%s' % (request.is_secure() and 'https' or 'http', 121 request.get_host()) 122 123 if 'HTTP_ORIGIN' in request.META: 124 # Cookies are unreliable when there are untrusted sites on sibling 125 # domains. foo.example.com can set a cookie scoped to .example.com 126 # that is then visible to bar.example.com. There is a proposal for a 127 # new Origin header which should be a better CSRF check. When the 128 # browser supports it, verify the match. It is currently implemented 129 # by WebKit-based browsers, and Mozilla has a ticket to add support. 130 origin = request.META['HTTP_ORIGIN'] 131 if good_origin != origin: 132 reason = REASON_BAD_ORIGIN % (origin, good_origin) 133 logger.warning('Forbidden (%s): %s' % (reason, request.path), 134 extra={ 135 'status_code': 403, 136 'request': request, 137 } 138 ) 139 return self._reject(request, reason) 140 118 141 if request.is_secure(): 119 142 # Suppose user visits http://example.com/ 120 143 # An active network attacker,(man-in-the-middle, MITM) sends a … … class CsrfViewMiddleware(object): 141 164 ) 142 165 return self._reject(request, REASON_NO_REFERER) 143 166 144 # Note that request.get_host() includes the port 145 good_referer = 'https://%s/' % request.get_host() 146 if not same_origin(referer, good_referer): 147 reason = REASON_BAD_REFERER % (referer, good_referer) 167 if not same_origin(referer, good_origin): 168 reason = REASON_BAD_REFERER % (referer, good_origin) 148 169 logger.warning('Forbidden (%s): %s' % (reason, request.path), 149 170 extra={ 150 171 'status_code': 403, -
docs/ref/contrib/csrf.txt
diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt index 013125a..dee0c37 100644
a b The CSRF protection is based on the following things: 213 213 done for HTTP requests because the presence of the Referer header is not 214 214 reliable enough under HTTP.) 215 215 216 5. When the browser provides the `Origin header`_, it is checked for the correct 217 origin as in the referer checking above. This provides protection against 218 cross-subdomain attacks for browsers which implement the header. 219 220 .. _Origin header: http://www.ietf.org/id/draft-ietf-websec-origin-00.txt 221 216 222 This ensures that only forms that have originated from your Web site can be used 217 223 to POST data back. 218 224 -
tests/regressiontests/csrf_tests/tests.py
diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index a98a6a4..751a0cc 100644
a b class CsrfViewMiddlewareTest(TestCase): 51 51 _csrf_id = "1" 52 52 53 53 def _get_GET_no_csrf_cookie_request(self): 54 return TestingHttpRequest() 54 req = TestingHttpRequest() 55 # Include some host so request.get_host() doesn't error. 56 req.META['HTTP_HOST'] = 'www.example.com' 57 return req 55 58 56 59 def _get_GET_csrf_cookie_request(self): 57 60 req = TestingHttpRequest() 61 # Include some host so request.get_host() doesn't error. 62 req.META['HTTP_HOST'] = 'www.example.com' 58 63 req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id_cookie 59 64 return req 60 65 … … class CsrfViewMiddlewareTest(TestCase): 249 254 req.META['HTTP_REFERER'] = 'https://www.example.com' 250 255 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 251 256 self.assertEqual(None, req2) 257 258 def test_bad_origin(self): 259 """ 260 Test that a request with a bad origin is rejected 261 """ 262 req = self._get_POST_request_with_token() 263 req.META['HTTP_HOST'] = 'www.example.com' 264 req.META['HTTP_ORIGIN'] = 'https://www.evil.org' 265 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 266 self.assertNotEqual(None, req2) 267 self.assertEqual(403, req2.status_code) 268 269 def test_bad_origin_2(self): 270 """ 271 Test that a request with a null origin is rejected 272 """ 273 req = self._get_POST_request_with_token() 274 req.META['HTTP_HOST'] = 'www.example.com' 275 req.META['HTTP_ORIGIN'] = 'null' 276 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 277 self.assertNotEqual(None, req2) 278 self.assertEqual(403, req2.status_code) 279 280 def test_bad_origin_3(self): 281 """ 282 Test that a request with an origin with wrong protocol is rejected 283 """ 284 req = self._get_POST_request_with_token() 285 req._is_secure = True 286 req.META['HTTP_HOST'] = 'www.example.com' 287 req.META['HTTP_ORIGIN'] = 'http://example.com' 288 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 289 self.assertNotEqual(None, req2) 290 self.assertEqual(403, req2.status_code) 291 292 def test_good_origin(self): 293 """ 294 Test that a POST HTTP request with a good origin is accepted 295 """ 296 req = self._get_POST_request_with_token() 297 req.META['HTTP_HOST'] = 'www.example.com' 298 req.META['HTTP_ORIGIN'] = 'http://www.example.com' 299 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 300 self.assertEqual(None, req2) 301 302 def test_good_origin_2(self): 303 """ 304 Test that a POST HTTPS request with a good origin is accepted 305 """ 306 req = self._get_POST_request_with_token() 307 req._is_secure = True 308 req.META['HTTP_HOST'] = 'www.example.com' 309 req.META['HTTP_ORIGIN'] = 'https://www.example.com' 310 req.META['HTTP_REFERER'] = 'https://www.example.com/somepage' 311 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 312 self.assertEqual(None, req2)