/tests/regressiontests/csrf_tests/tests.py
Python | 397 lines | 393 code | 2 blank | 2 comment | 0 complexity | ba30fee6ae142ed1dc939e81e393f764 MD5 | raw file
1# -*- coding: utf-8 -*- 2import warnings 3 4from django.test import TestCase 5from django.http import HttpRequest, HttpResponse 6from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware 7from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, requires_csrf_token 8from django.core.context_processors import csrf 9from django.contrib.sessions.middleware import SessionMiddleware 10from django.utils.importlib import import_module 11from django.conf import settings 12from django.template import RequestContext, Template 13 14# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests 15def post_form_response(): 16 resp = HttpResponse(content=u""" 17<html><body><h1>\u00a1Unicode!<form method="post"><input type="text" /></form></body></html> 18""", mimetype="text/html") 19 return resp 20 21def post_form_response_non_html(): 22 resp = post_form_response() 23 resp["Content-Type"] = "application/xml" 24 return resp 25 26def post_form_view(request): 27 """A view that returns a POST form (without a token)""" 28 return post_form_response() 29 30# Response/views used for template tag tests 31def _token_template(): 32 return Template("{% csrf_token %}") 33 34def _render_csrf_token_template(req): 35 context = RequestContext(req, processors=[csrf]) 36 template = _token_template() 37 return template.render(context) 38 39def token_view(request): 40 """A view that uses {% csrf_token %}""" 41 return HttpResponse(_render_csrf_token_template(request)) 42 43def non_token_view_using_request_processor(request): 44 """ 45 A view that doesn't use the token, but does use the csrf view processor. 46 """ 47 context = RequestContext(request, processors=[csrf]) 48 template = Template("") 49 return HttpResponse(template.render(context)) 50 51class TestingHttpRequest(HttpRequest): 52 """ 53 A version of HttpRequest that allows us to change some things 54 more easily 55 """ 56 def is_secure(self): 57 return getattr(self, '_is_secure', False) 58 59class CsrfMiddlewareTest(TestCase): 60 # The csrf token is potentially from an untrusted source, so could have 61 # characters that need dealing with. 62 _csrf_id_cookie = "<1>\xc2\xa1" 63 _csrf_id = "1" 64 65 # This is a valid session token for this ID and secret key. This was generated using 66 # the old code that we're to be backwards-compatible with. Don't use the CSRF code 67 # to generate this hash, or we're merely testing the code against itself and not 68 # checking backwards-compatibility. This is also the output of (echo -n test1 | md5sum). 69 _session_token = "5a105e8b9d40e1329780d62ea2265d8a" 70 _session_id = "1" 71 _secret_key_for_session_test= "test" 72 73 def setUp(self): 74 self.save_warnings_state() 75 warnings.filterwarnings('ignore', category=DeprecationWarning, 76 module='django.middleware.csrf') 77 78 def tearDown(self): 79 self.restore_warnings_state() 80 81 def _get_GET_no_csrf_cookie_request(self): 82 return TestingHttpRequest() 83 84 def _get_GET_csrf_cookie_request(self): 85 req = TestingHttpRequest() 86 req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id_cookie 87 return req 88 89 def _get_POST_csrf_cookie_request(self): 90 req = self._get_GET_csrf_cookie_request() 91 req.method = "POST" 92 return req 93 94 def _get_POST_no_csrf_cookie_request(self): 95 req = self._get_GET_no_csrf_cookie_request() 96 req.method = "POST" 97 return req 98 99 def _get_POST_request_with_token(self): 100 req = self._get_POST_csrf_cookie_request() 101 req.POST['csrfmiddlewaretoken'] = self._csrf_id 102 return req 103 104 def _get_POST_session_request_with_token(self): 105 req = self._get_POST_no_csrf_cookie_request() 106 req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id 107 req.POST['csrfmiddlewaretoken'] = self._session_token 108 return req 109 110 def _get_POST_session_request_no_token(self): 111 req = self._get_POST_no_csrf_cookie_request() 112 req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id 113 return req 114 115 def _check_token_present(self, response, csrf_id=None): 116 self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id)) 117 118 # Check the post processing and outgoing cookie 119 def test_process_response_no_csrf_cookie(self): 120 """ 121 When no prior CSRF cookie exists, check that the cookie is created and a 122 token is inserted. 123 """ 124 req = self._get_GET_no_csrf_cookie_request() 125 CsrfMiddleware().process_view(req, post_form_view, (), {}) 126 127 resp = post_form_response() 128 resp_content = resp.content # needed because process_response modifies resp 129 resp2 = CsrfMiddleware().process_response(req, resp) 130 131 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 132 self.assertNotEqual(csrf_cookie, False) 133 self.assertNotEqual(resp_content, resp2.content) 134 self._check_token_present(resp2, csrf_cookie.value) 135 # Check the Vary header got patched correctly 136 self.assertTrue('Cookie' in resp2.get('Vary','')) 137 138 def test_process_response_for_exempt_view(self): 139 """ 140 Check that a view decorated with 'csrf_view_exempt' is still 141 post-processed to add the CSRF token. 142 """ 143 req = self._get_GET_no_csrf_cookie_request() 144 CsrfMiddleware().process_view(req, csrf_view_exempt(post_form_view), (), {}) 145 146 resp = post_form_response() 147 resp_content = resp.content # needed because process_response modifies resp 148 resp2 = CsrfMiddleware().process_response(req, resp) 149 150 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 151 self.assertNotEqual(csrf_cookie, False) 152 self.assertNotEqual(resp_content, resp2.content) 153 self._check_token_present(resp2, csrf_cookie.value) 154 155 def test_process_response_no_csrf_cookie_view_only_get_token_used(self): 156 """ 157 When no prior CSRF cookie exists, check that the cookie is created, even 158 if only CsrfViewMiddleware is used. 159 """ 160 # This is checking that CsrfViewMiddleware has the cookie setting 161 # code. Most of the other tests use CsrfMiddleware. 162 req = self._get_GET_no_csrf_cookie_request() 163 # token_view calls get_token() indirectly 164 CsrfViewMiddleware().process_view(req, token_view, (), {}) 165 resp = token_view(req) 166 resp2 = CsrfViewMiddleware().process_response(req, resp) 167 168 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 169 self.assertNotEqual(csrf_cookie, False) 170 171 def test_process_response_get_token_not_used(self): 172 """ 173 Check that if get_token() is not called, the view middleware does not 174 add a cookie. 175 """ 176 # This is important to make pages cacheable. Pages which do call 177 # get_token(), assuming they use the token, are not cacheable because 178 # the token is specific to the user 179 req = self._get_GET_no_csrf_cookie_request() 180 # non_token_view_using_request_processor does not call get_token(), but 181 # does use the csrf request processor. By using this, we are testing 182 # that the view processor is properly lazy and doesn't call get_token() 183 # until needed. 184 CsrfViewMiddleware().process_view(req, non_token_view_using_request_processor, (), {}) 185 resp = non_token_view_using_request_processor(req) 186 resp2 = CsrfViewMiddleware().process_response(req, resp) 187 188 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 189 self.assertEqual(csrf_cookie, False) 190 191 def test_process_response_existing_csrf_cookie(self): 192 """ 193 Check that the token is inserted when a prior CSRF cookie exists 194 """ 195 req = self._get_GET_csrf_cookie_request() 196 CsrfMiddleware().process_view(req, post_form_view, (), {}) 197 198 resp = post_form_response() 199 resp_content = resp.content # needed because process_response modifies resp 200 resp2 = CsrfMiddleware().process_response(req, resp) 201 self.assertNotEqual(resp_content, resp2.content) 202 self._check_token_present(resp2) 203 204 def test_process_response_non_html(self): 205 """ 206 Check the the post-processor does nothing for content-types not in _HTML_TYPES. 207 """ 208 req = self._get_GET_no_csrf_cookie_request() 209 CsrfMiddleware().process_view(req, post_form_view, (), {}) 210 resp = post_form_response_non_html() 211 resp_content = resp.content # needed because process_response modifies resp 212 resp2 = CsrfMiddleware().process_response(req, resp) 213 self.assertEqual(resp_content, resp2.content) 214 215 def test_process_response_exempt_view(self): 216 """ 217 Check that no post processing is done for an exempt view 218 """ 219 req = self._get_GET_csrf_cookie_request() 220 view = csrf_exempt(post_form_view) 221 CsrfMiddleware().process_view(req, view, (), {}) 222 223 resp = view(req) 224 resp_content = resp.content 225 resp2 = CsrfMiddleware().process_response(req, resp) 226 self.assertEqual(resp_content, resp2.content) 227 228 # Check the request processing 229 def test_process_request_no_session_no_csrf_cookie(self): 230 """ 231 Check that if neither a CSRF cookie nor a session cookie are present, 232 the middleware rejects the incoming request. This will stop login CSRF. 233 """ 234 req = self._get_POST_no_csrf_cookie_request() 235 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 236 self.assertEqual(403, req2.status_code) 237 238 def test_process_request_csrf_cookie_no_token(self): 239 """ 240 Check that if a CSRF cookie is present but no token, the middleware 241 rejects the incoming request. 242 """ 243 req = self._get_POST_csrf_cookie_request() 244 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 245 self.assertEqual(403, req2.status_code) 246 247 def test_process_request_csrf_cookie_and_token(self): 248 """ 249 Check that if both a cookie and a token is present, the middleware lets it through. 250 """ 251 req = self._get_POST_request_with_token() 252 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 253 self.assertEqual(None, req2) 254 255 def test_process_request_session_cookie_no_csrf_cookie_token(self): 256 """ 257 When no CSRF cookie exists, but the user has a session, check that a token 258 using the session cookie as a legacy CSRF cookie is accepted. 259 """ 260 orig_secret_key = settings.SECRET_KEY 261 settings.SECRET_KEY = self._secret_key_for_session_test 262 try: 263 req = self._get_POST_session_request_with_token() 264 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 265 self.assertEqual(None, req2) 266 finally: 267 settings.SECRET_KEY = orig_secret_key 268 269 def test_process_request_session_cookie_no_csrf_cookie_no_token(self): 270 """ 271 Check that if a session cookie is present but no token and no CSRF cookie, 272 the request is rejected. 273 """ 274 req = self._get_POST_session_request_no_token() 275 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 276 self.assertEqual(403, req2.status_code) 277 278 def test_process_request_csrf_cookie_no_token_exempt_view(self): 279 """ 280 Check that if a CSRF cookie is present and no token, but the csrf_exempt 281 decorator has been applied to the view, the middleware lets it through 282 """ 283 req = self._get_POST_csrf_cookie_request() 284 req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {}) 285 self.assertEqual(None, req2) 286 287 def test_csrf_token_in_header(self): 288 """ 289 Check that we can pass in the token in a header instead of in the form 290 """ 291 req = self._get_POST_csrf_cookie_request() 292 req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id 293 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 294 self.assertEqual(None, req2) 295 296 # Tests for the template tag method 297 def test_token_node_no_csrf_cookie(self): 298 """ 299 Check that CsrfTokenNode works when no CSRF cookie is set 300 """ 301 req = self._get_GET_no_csrf_cookie_request() 302 resp = token_view(req) 303 self.assertEqual(u"", resp.content) 304 305 def test_token_node_empty_csrf_cookie(self): 306 """ 307 Check that we get a new token if the csrf_cookie is the empty string 308 """ 309 req = self._get_GET_no_csrf_cookie_request() 310 req.COOKIES[settings.CSRF_COOKIE_NAME] = "" 311 CsrfViewMiddleware().process_view(req, token_view, (), {}) 312 resp = token_view(req) 313 314 self.assertNotEqual(u"", resp.content) 315 316 def test_token_node_with_csrf_cookie(self): 317 """ 318 Check that CsrfTokenNode works when a CSRF cookie is set 319 """ 320 req = self._get_GET_csrf_cookie_request() 321 CsrfViewMiddleware().process_view(req, token_view, (), {}) 322 resp = token_view(req) 323 self._check_token_present(resp) 324 325 def test_get_token_for_exempt_view(self): 326 """ 327 Check that get_token still works for a view decorated with 'csrf_view_exempt'. 328 """ 329 req = self._get_GET_csrf_cookie_request() 330 CsrfViewMiddleware().process_view(req, csrf_view_exempt(token_view), (), {}) 331 resp = token_view(req) 332 self._check_token_present(resp) 333 334 def test_get_token_for_requires_csrf_token_view(self): 335 """ 336 Check that get_token works for a view decorated solely with requires_csrf_token 337 """ 338 req = self._get_GET_csrf_cookie_request() 339 resp = requires_csrf_token(token_view)(req) 340 self._check_token_present(resp) 341 342 def test_token_node_with_new_csrf_cookie(self): 343 """ 344 Check that CsrfTokenNode works when a CSRF cookie is created by 345 the middleware (when one was not already present) 346 """ 347 req = self._get_GET_no_csrf_cookie_request() 348 CsrfViewMiddleware().process_view(req, token_view, (), {}) 349 resp = token_view(req) 350 resp2 = CsrfViewMiddleware().process_response(req, resp) 351 csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME] 352 self._check_token_present(resp, csrf_id=csrf_cookie.value) 353 354 def test_response_middleware_without_view_middleware(self): 355 """ 356 Check that CsrfResponseMiddleware finishes without error if the view middleware 357 has not been called, as is the case if a request middleware returns a response. 358 """ 359 req = self._get_GET_no_csrf_cookie_request() 360 resp = post_form_view(req) 361 CsrfMiddleware().process_response(req, resp) 362 363 def test_https_bad_referer(self): 364 """ 365 Test that a POST HTTPS request with a bad referer is rejected 366 """ 367 req = self._get_POST_request_with_token() 368 req._is_secure = True 369 req.META['HTTP_HOST'] = 'www.example.com' 370 req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage' 371 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 372 self.assertNotEqual(None, req2) 373 self.assertEqual(403, req2.status_code) 374 375 def test_https_good_referer(self): 376 """ 377 Test that a POST HTTPS request with a good referer is accepted 378 """ 379 req = self._get_POST_request_with_token() 380 req._is_secure = True 381 req.META['HTTP_HOST'] = 'www.example.com' 382 req.META['HTTP_REFERER'] = 'https://www.example.com/somepage' 383 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 384 self.assertEqual(None, req2) 385 386 def test_https_good_referer_2(self): 387 """ 388 Test that a POST HTTPS request with a good referer is accepted 389 where the referer contains no trailing slash 390 """ 391 # See ticket #15617 392 req = self._get_POST_request_with_token() 393 req._is_secure = True 394 req.META['HTTP_HOST'] = 'www.example.com' 395 req.META['HTTP_REFERER'] = 'https://www.example.com' 396 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 397 self.assertEqual(None, req2)