Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Emulate browser's cookie domains behaviour

For compatibility with cookie domain setting, origin check emulates
the behaviour of browser cookie-domain validator.

Signed-off-by: Rohan Jain <crodjer@gmail.com>
  • Loading branch information...
commit 46a2f7b1768712e7c723e047beb71bb9b59610bb 1 parent 13ba162
@crodjer authored
View
21 django/middleware/csrf.py
@@ -116,12 +116,25 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
host = request.META.get('HTTP_HOST', '')
origin = request.META.get('HTTP_ORIGIN')
- cookie_domain = getattr(settings, 'CSRF_COOKIE_DOMAIN') or host
+ good_origin = settings.CSRF_COOKIE_DOMAIN or host
+ # If origin header exists, use it to check for csrf attacks.
+ # Origin header is being compared to None here as we need to reject
+ # requests with origin header as '' too, which otherwise is treated
+ # as null.
if origin is not None:
- # TODO: Fix this check to be like browser cookie check
- if not origin.endswith(cookie_domain):
- reason = REASON_BAD_ORIGIN % (origin, cookie_domain)
+
+ # If the good origin starts with a dot (.), it means the cookie
+ # is supposed to work across all the subdomains, i.e. an
+ # endswith test and a count of dots should be fine here.
+ # In case case the actual and the good origin are the same, then
+ # too it passes.
+ if not ((good_origin.startswith('.')
+ and good_origin.count('.') is origin.count('.')
+ and origin.endswith(good_origin))
+ or origin[origin.find('://')+3:] == good_origin):
+
+ reason = REASON_BAD_ORIGIN % (origin, good_origin)
logger.warning('Forbidden (%s): %s',
reason, request.path,
extra={
View
59 tests/regressiontests/csrf_tests/tests.py
@@ -7,6 +7,7 @@
from django.template import RequestContext, Template
from django.test import TestCase
from django.views.decorators.csrf import csrf_exempt, requires_csrf_token, ensure_csrf_cookie
+from django.test.utils import override_settings
# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests
@@ -334,9 +335,42 @@ def view(request):
self.assertTrue(resp2.cookies.get(settings.CSRF_COOKIE_NAME, False))
self.assertTrue('Cookie' in resp2.get('Vary',''))
+ @override_settings(CSRF_COOKIE_DOMAIN='.example.com')
def test_good_origin_header(self):
"""
- Test if a good origin header is accepted.
+ Test if a good origin header is accepted for across subdomain settings.
+ """
+ req = self._get_POST_request_with_token()
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_ORIGIN'] = 'http://www.example.com'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(None, req2)
+
+ @override_settings(CSRF_COOKIE_DOMAIN='www.example.com')
+ def test_good_origin_header_2(self):
+ """
+ Test if a good origin header is accepted for a single subdomain.
+ """
+ req = self._get_POST_request_with_token()
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_ORIGIN'] = 'http://www.example.com'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(None, req2)
+
+ @override_settings(CSRF_COOKIE_DOMAIN='example.com')
+ def test_good_origin_header_3(self):
+ """
+ Test if a good origin header is accepted for a no subdomain.
+ """
+ req = self._get_POST_request_with_token()
+ req.META['HTTP_HOST'] = 'example.com'
+ req.META['HTTP_ORIGIN'] = 'http://example.com'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(None, req2)
+
+ def test_good_origin_header_4(self):
+ """
+ Test if a good origin header is accepted for no cookie setting.
"""
req = self._get_POST_request_with_token()
req.META['HTTP_HOST'] = 'www.example.com'
@@ -346,7 +380,28 @@ def test_good_origin_header(self):
def test_bad_origin_header(self):
"""
- Test if a bad origin header is rejected.
+ Test if a bad origin header is rejected for different domain.
+ """
+ req = self._get_POST_request_with_token()
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_ORIGIN'] = 'http://www.evil.com'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(403, req2.status_code)
+
+ @override_settings(CSRF_COOKIE_DOMAIN='example.com')
+ def test_bad_origin_header_2(self):
+ """
+ Test if a bad origin header is rejected for subdomains.
+ """
+ req = self._get_POST_request_with_token()
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_ORIGIN'] = 'http://www.example.com'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEqual(403, req2.status_code)
+
+ def test_bad_origin_header_3(self):
+ """
+ Test if a bad origin header is rejected with no cookie setting.
"""
req = self._get_POST_request_with_token()
req.META['HTTP_HOST'] = 'www.example.com'
Please sign in to comment.
Something went wrong with that request. Please try again.