summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMatt Robenolt <matt@ydekproductions.com>2015-03-17 02:52:55 -0700
committerTim Graham <timograham@gmail.com>2015-09-16 12:21:50 -0400
commitb0c56b895fd2694d7f5d4595bdbbc41916607f45 (patch)
tree4ac4ef6e9e3cc89263f99ef76321ca88b2968a1c /tests
parent535809e12161d28dacaf5161436fc05a9bb064aa (diff)
downloaddjango-b0c56b895fd2694d7f5d4595bdbbc41916607f45.tar.gz
Fixed #24496 -- Added CSRF Referer checking against CSRF_COOKIE_DOMAIN.
Thanks Seth Gottlieb for help with the documentation and Carl Meyer and Joshua Kehn for reviews.
Diffstat (limited to 'tests')
-rw-r--r--tests/csrf_tests/tests.py105
-rw-r--r--tests/utils_tests/test_http.py44
2 files changed, 111 insertions, 38 deletions
diff --git a/tests/csrf_tests/tests.py b/tests/csrf_tests/tests.py
index 382242d6a4..6c6f49d2b8 100644
--- a/tests/csrf_tests/tests.py
+++ b/tests/csrf_tests/tests.py
@@ -295,7 +295,7 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME]
self._check_token_present(resp, csrf_id=csrf_cookie.value)
- @override_settings(ALLOWED_HOSTS=['www.example.com'])
+ @override_settings(DEBUG=True)
def test_https_bad_referer(self):
"""
Test that a POST HTTPS request with a bad referer is rejected
@@ -304,27 +304,50 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
req._is_secure_override = True
req.META['HTTP_HOST'] = 'www.example.com'
req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage'
- req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
- self.assertIsNotNone(req2)
- self.assertEqual(403, req2.status_code)
-
- @override_settings(ALLOWED_HOSTS=['www.example.com'])
+ req.META['SERVER_PORT'] = '443'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(
+ response,
+ 'Referer checking failed - https://www.evil.org/somepage does not '
+ 'match any trusted origins.',
+ status_code=403,
+ )
+
+ @override_settings(DEBUG=True)
def test_https_malformed_referer(self):
"""
A POST HTTPS request with a bad referer is rejected.
"""
+ malformed_referer_msg = 'Referer checking failed - Referer is malformed.'
req = self._get_POST_request_with_token()
req._is_secure_override = True
- req.META['HTTP_HOST'] = 'www.example.com'
req.META['HTTP_REFERER'] = 'http://http://www.example.com/'
- req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
- self.assertIsNotNone(req2)
- self.assertEqual(403, req2.status_code)
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(
+ response,
+ 'Referer checking failed - Referer is insecure while host is secure.',
+ status_code=403,
+ )
+ # Empty
+ req.META['HTTP_REFERER'] = ''
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(response, malformed_referer_msg, status_code=403)
# Non-ASCII
req.META['HTTP_REFERER'] = b'\xd8B\xf6I\xdf'
- req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
- self.assertIsNotNone(req2)
- self.assertEqual(403, req2.status_code)
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(response, malformed_referer_msg, status_code=403)
+ # missing scheme
+ # >>> urlparse('//example.com/')
+ # ParseResult(scheme='', netloc='example.com', path='/', params='', query='', fragment='')
+ req.META['HTTP_REFERER'] = '//example.com/'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(response, malformed_referer_msg, status_code=403)
+ # missing netloc
+ # >>> urlparse('https://')
+ # ParseResult(scheme='https', netloc='', path='', params='', query='', fragment='')
+ req.META['HTTP_REFERER'] = 'https://'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(response, malformed_referer_msg, status_code=403)
@override_settings(ALLOWED_HOSTS=['www.example.com'])
def test_https_good_referer(self):
@@ -365,6 +388,62 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
self.assertIsNone(req2)
+ @override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_TRUSTED_ORIGINS=['.example.com'])
+ def test_https_csrf_wildcard_trusted_origin_allowed(self):
+ """
+ A POST HTTPS request with a referer that matches a CSRF_TRUSTED_ORIGINS
+ wilcard is accepted.
+ """
+ req = self._get_POST_request_with_token()
+ req._is_secure_override = True
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_REFERER'] = 'https://dashboard.example.com'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertIsNone(response)
+
+ @override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_COOKIE_DOMAIN='.example.com')
+ def test_https_good_referer_matches_cookie_domain(self):
+ """
+ A POST HTTPS request with a good referer should be accepted from a
+ subdomain that's allowed by CSRF_COOKIE_DOMAIN.
+ """
+ req = self._get_POST_request_with_token()
+ req._is_secure_override = True
+ req.META['HTTP_REFERER'] = 'https://foo.example.com/'
+ req.META['SERVER_PORT'] = '443'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertIsNone(response)
+
+ @override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_COOKIE_DOMAIN='.example.com')
+ def test_https_good_referer_matches_cookie_domain_with_different_port(self):
+ """
+ A POST HTTPS request with a good referer should be accepted from a
+ subdomain that's allowed by CSRF_COOKIE_DOMAIN and a non-443 port.
+ """
+ req = self._get_POST_request_with_token()
+ req._is_secure_override = True
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_REFERER'] = 'https://foo.example.com:4443/'
+ req.META['SERVER_PORT'] = '4443'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertIsNone(response)
+
+ @override_settings(CSRF_COOKIE_DOMAIN='.example.com', DEBUG=True)
+ def test_https_reject_insecure_referer(self):
+ """
+ A POST HTTPS request from an insecure referer should be rejected.
+ """
+ req = self._get_POST_request_with_token()
+ req._is_secure_override = True
+ req.META['HTTP_REFERER'] = 'http://example.com/'
+ req.META['SERVER_PORT'] = '443'
+ response = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertContains(
+ response,
+ 'Referer checking failed - Referer is insecure while host is secure.',
+ status_code=403,
+ )
+
def test_ensures_csrf_cookie_no_middleware(self):
"""
The ensure_csrf_cookie() decorator works without middleware.
diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py
index 74c6905294..baa126d423 100644
--- a/tests/utils_tests/test_http.py
+++ b/tests/utils_tests/test_http.py
@@ -10,31 +10,6 @@ from django.utils.datastructures import MultiValueDict
class TestUtilsHttp(unittest.TestCase):
- def test_same_origin_true(self):
- # Identical
- self.assertTrue(http.same_origin('http://foo.com/', 'http://foo.com/'))
- # One with trailing slash - see #15617
- self.assertTrue(http.same_origin('http://foo.com', 'http://foo.com/'))
- self.assertTrue(http.same_origin('http://foo.com/', 'http://foo.com'))
- # With port
- self.assertTrue(http.same_origin('https://foo.com:8000', 'https://foo.com:8000/'))
- # No port given but according to RFC6454 still the same origin
- self.assertTrue(http.same_origin('http://foo.com', 'http://foo.com:80/'))
- self.assertTrue(http.same_origin('https://foo.com', 'https://foo.com:443/'))
-
- def test_same_origin_false(self):
- # Different scheme
- self.assertFalse(http.same_origin('http://foo.com', 'https://foo.com'))
- # Different host
- self.assertFalse(http.same_origin('http://foo.com', 'http://goo.com'))
- # Different host again
- self.assertFalse(http.same_origin('http://foo.com', 'http://foo.com.evil.com'))
- # Different port
- self.assertFalse(http.same_origin('http://foo.com:8000', 'http://foo.com:8001'))
- # No port given
- self.assertFalse(http.same_origin('http://foo.com', 'http://foo.com:8000/'))
- self.assertFalse(http.same_origin('https://foo.com', 'https://foo.com:8000/'))
-
def test_urlencode(self):
# 2-tuples (the norm)
result = http.urlencode((('a', 1), ('b', 2), ('c', 3)))
@@ -157,6 +132,25 @@ class TestUtilsHttp(unittest.TestCase):
http.urlunquote_plus('Paris+&+Orl%C3%A9ans'),
'Paris & Orl\xe9ans')
+ def test_is_same_domain_good(self):
+ for pair in (
+ ('example.com', 'example.com'),
+ ('example.com', '.example.com'),
+ ('foo.example.com', '.example.com'),
+ ('example.com:8888', 'example.com:8888'),
+ ('example.com:8888', '.example.com:8888'),
+ ('foo.example.com:8888', '.example.com:8888'),
+ ):
+ self.assertTrue(http.is_same_domain(*pair))
+
+ def test_is_same_domain_bad(self):
+ for pair in (
+ ('example2.com', 'example.com'),
+ ('foo.example.com', 'example.com'),
+ ('example.com:9999', 'example.com:8888'),
+ ):
+ self.assertFalse(http.is_same_domain(*pair))
+
class ETagProcessingTests(unittest.TestCase):
def test_parsing(self):