diff options
| author | Carl Meyer <carl@oddbird.net> | 2013-02-09 11:32:51 -0700 |
|---|---|---|
| committer | Carl Meyer <carl@oddbird.net> | 2013-02-19 10:37:54 -0700 |
| commit | 9936fdb11d0bbf0bd242f259bfb97bbf849d16f8 (patch) | |
| tree | 43770281aab2cce7da09de070c347235d7d31ebe /tests | |
| parent | 57b62a74cb44ac3cf8b1a9d4cf75bfe953208814 (diff) | |
[1.4.x] Added ALLOWED_HOSTS setting for HTTP host header validation.
This is a security fix; disclosure and advisory coming shortly.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/regressiontests/csrf_tests/tests.py | 4 | ||||
| -rw-r--r-- | tests/regressiontests/requests/tests.py | 257 |
2 files changed, 136 insertions, 125 deletions
diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 71400ead89..a605134fba 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -7,6 +7,7 @@ from django.http import HttpRequest, HttpResponse from django.middleware.csrf import CsrfViewMiddleware, CSRF_KEY_LENGTH from django.template import RequestContext, Template from django.test import TestCase +from django.test.utils import override_settings from django.views.decorators.csrf import csrf_exempt, requires_csrf_token, ensure_csrf_cookie @@ -267,6 +268,7 @@ class CsrfViewMiddlewareTest(TestCase): 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']) def test_https_bad_referer(self): """ Test that a POST HTTPS request with a bad referer is rejected @@ -279,6 +281,7 @@ class CsrfViewMiddlewareTest(TestCase): self.assertNotEqual(None, req2) self.assertEqual(403, req2.status_code) + @override_settings(ALLOWED_HOSTS=['www.example.com']) def test_https_good_referer(self): """ Test that a POST HTTPS request with a good referer is accepted @@ -290,6 +293,7 @@ class CsrfViewMiddlewareTest(TestCase): req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertEqual(None, req2) + @override_settings(ALLOWED_HOSTS=['www.example.com']) def test_https_good_referer_2(self): """ Test that a POST HTTPS request with a good referer is accepted diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py index 5927cbb8bc..2c9873c9e2 100644 --- a/tests/regressiontests/requests/tests.py +++ b/tests/regressiontests/requests/tests.py @@ -14,7 +14,7 @@ from django.core.exceptions import SuspiciousOperation from django.core.handlers.wsgi import WSGIRequest, LimitedStream from django.http import HttpRequest, HttpResponse, parse_cookie, build_request_repr, UnreadablePostError from django.test import TransactionTestCase -from django.test.utils import get_warnings_state, restore_warnings_state +from django.test.utils import get_warnings_state, restore_warnings_state, override_settings from django.utils import unittest from django.utils.http import cookie_date from django.utils.timezone import utc @@ -109,161 +109,168 @@ class RequestsTests(unittest.TestCase): self.assertEqual(request.build_absolute_uri(location="/path/with:colons"), 'http://www.example.com/path/with:colons') + @override_settings( + USE_X_FORWARDED_HOST=False, + ALLOWED_HOSTS=[ + 'forward.com', 'example.com', 'internal.com', '12.34.56.78', + '[2001:19f0:feee::dead:beef:cafe]', 'xn--4ca9at.com', + '.multitenant.com', 'INSENSITIVE.com', + ]) def test_http_get_host(self): - old_USE_X_FORWARDED_HOST = settings.USE_X_FORWARDED_HOST - try: - settings.USE_X_FORWARDED_HOST = False + # Check if X_FORWARDED_HOST is provided. + request = HttpRequest() + request.META = { + 'HTTP_X_FORWARDED_HOST': 'forward.com', + 'HTTP_HOST': 'example.com', + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 80, + } + # X_FORWARDED_HOST is ignored. + self.assertEqual(request.get_host(), 'example.com') - # Check if X_FORWARDED_HOST is provided. - request = HttpRequest() - request.META = { - u'HTTP_X_FORWARDED_HOST': u'forward.com', - u'HTTP_HOST': u'example.com', - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 80, - } - # X_FORWARDED_HOST is ignored. - self.assertEqual(request.get_host(), 'example.com') + # Check if X_FORWARDED_HOST isn't provided. + request = HttpRequest() + request.META = { + 'HTTP_HOST': 'example.com', + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 80, + } + self.assertEqual(request.get_host(), 'example.com') - # Check if X_FORWARDED_HOST isn't provided. - request = HttpRequest() - request.META = { - u'HTTP_HOST': u'example.com', - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 80, - } - self.assertEqual(request.get_host(), 'example.com') + # Check if HTTP_HOST isn't provided. + request = HttpRequest() + request.META = { + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 80, + } + self.assertEqual(request.get_host(), 'internal.com') - # Check if HTTP_HOST isn't provided. - request = HttpRequest() - request.META = { - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 80, - } - self.assertEqual(request.get_host(), 'internal.com') + # Check if HTTP_HOST isn't provided, and we're on a nonstandard port + request = HttpRequest() + request.META = { + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 8042, + } + self.assertEqual(request.get_host(), 'internal.com:8042') + + # Poisoned host headers are rejected as suspicious + legit_hosts = [ + 'example.com', + 'example.com:80', + '12.34.56.78', + '12.34.56.78:443', + '[2001:19f0:feee::dead:beef:cafe]', + '[2001:19f0:feee::dead:beef:cafe]:8080', + 'xn--4ca9at.com', # Punnycode for öäü.com + 'anything.multitenant.com', + 'multitenant.com', + 'insensitive.com', + ] - # Check if HTTP_HOST isn't provided, and we're on a nonstandard port + poisoned_hosts = [ + 'example.com@evil.tld', + 'example.com:dr.frankenstein@evil.tld', + 'example.com:dr.frankenstein@evil.tld:80', + 'example.com:80/badpath', + 'example.com: recovermypassword.com', + 'other.com', # not in ALLOWED_HOSTS + ] + + for host in legit_hosts: request = HttpRequest() request.META = { - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 8042, + 'HTTP_HOST': host, } - self.assertEqual(request.get_host(), 'internal.com:8042') - - # Poisoned host headers are rejected as suspicious - legit_hosts = [ - 'example.com', - 'example.com:80', - '12.34.56.78', - '12.34.56.78:443', - '[2001:19f0:feee::dead:beef:cafe]', - '[2001:19f0:feee::dead:beef:cafe]:8080', - 'xn--4ca9at.com', # Punnycode for öäü.com - ] - - poisoned_hosts = [ - 'example.com@evil.tld', - 'example.com:dr.frankenstein@evil.tld', - 'example.com:dr.frankenstein@evil.tld:80', - 'example.com:80/badpath', - 'example.com: recovermypassword.com', - ] + request.get_host() - for host in legit_hosts: + for host in poisoned_hosts: + with self.assertRaises(SuspiciousOperation): request = HttpRequest() request.META = { 'HTTP_HOST': host, } request.get_host() - for host in poisoned_hosts: - with self.assertRaises(SuspiciousOperation): - request = HttpRequest() - request.META = { - 'HTTP_HOST': host, - } - request.get_host() + @override_settings(USE_X_FORWARDED_HOST=True, ALLOWED_HOSTS=['*']) + def test_http_get_host_with_x_forwarded_host(self): + # Check if X_FORWARDED_HOST is provided. + request = HttpRequest() + request.META = { + 'HTTP_X_FORWARDED_HOST': 'forward.com', + 'HTTP_HOST': 'example.com', + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 80, + } + # X_FORWARDED_HOST is obeyed. + self.assertEqual(request.get_host(), 'forward.com') - finally: - settings.USE_X_FORWARDED_HOST = old_USE_X_FORWARDED_HOST + # Check if X_FORWARDED_HOST isn't provided. + request = HttpRequest() + request.META = { + 'HTTP_HOST': 'example.com', + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 80, + } + self.assertEqual(request.get_host(), 'example.com') - def test_http_get_host_with_x_forwarded_host(self): - old_USE_X_FORWARDED_HOST = settings.USE_X_FORWARDED_HOST - try: - settings.USE_X_FORWARDED_HOST = True + # Check if HTTP_HOST isn't provided. + request = HttpRequest() + request.META = { + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 80, + } + self.assertEqual(request.get_host(), 'internal.com') - # Check if X_FORWARDED_HOST is provided. - request = HttpRequest() - request.META = { - u'HTTP_X_FORWARDED_HOST': u'forward.com', - u'HTTP_HOST': u'example.com', - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 80, - } - # X_FORWARDED_HOST is obeyed. - self.assertEqual(request.get_host(), 'forward.com') + # Check if HTTP_HOST isn't provided, and we're on a nonstandard port + request = HttpRequest() + request.META = { + 'SERVER_NAME': 'internal.com', + 'SERVER_PORT': 8042, + } + self.assertEqual(request.get_host(), 'internal.com:8042') - # Check if X_FORWARDED_HOST isn't provided. - request = HttpRequest() - request.META = { - u'HTTP_HOST': u'example.com', - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 80, - } - self.assertEqual(request.get_host(), 'example.com') + # Poisoned host headers are rejected as suspicious + legit_hosts = [ + 'example.com', + 'example.com:80', + '12.34.56.78', + '12.34.56.78:443', + '[2001:19f0:feee::dead:beef:cafe]', + '[2001:19f0:feee::dead:beef:cafe]:8080', + 'xn--4ca9at.com', # Punnycode for öäü.com + ] - # Check if HTTP_HOST isn't provided. - request = HttpRequest() - request.META = { - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 80, - } - self.assertEqual(request.get_host(), 'internal.com') + poisoned_hosts = [ + 'example.com@evil.tld', + 'example.com:dr.frankenstein@evil.tld', + 'example.com:dr.frankenstein@evil.tld:80', + 'example.com:80/badpath', + 'example.com: recovermypassword.com', + ] - # Check if HTTP_HOST isn't provided, and we're on a nonstandard port + for host in legit_hosts: request = HttpRequest() request.META = { - u'SERVER_NAME': u'internal.com', - u'SERVER_PORT': 8042, + 'HTTP_HOST': host, } - self.assertEqual(request.get_host(), 'internal.com:8042') + request.get_host() - # Poisoned host headers are rejected as suspicious - legit_hosts = [ - 'example.com', - 'example.com:80', - '12.34.56.78', - '12.34.56.78:443', - '[2001:19f0:feee::dead:beef:cafe]', - '[2001:19f0:feee::dead:beef:cafe]:8080', - 'xn--4ca9at.com', # Punnycode for öäü.com - ] - - poisoned_hosts = [ - 'example.com@evil.tld', - 'example.com:dr.frankenstein@evil.tld', - 'example.com:dr.frankenstein@evil.tld:80', - 'example.com:80/badpath', - 'example.com: recovermypassword.com', - ] - - for host in legit_hosts: + for host in poisoned_hosts: + with self.assertRaises(SuspiciousOperation): request = HttpRequest() request.META = { 'HTTP_HOST': host, } request.get_host() - for host in poisoned_hosts: - with self.assertRaises(SuspiciousOperation): - request = HttpRequest() - request.META = { - 'HTTP_HOST': host, - } - request.get_host() - - finally: - settings.USE_X_FORWARDED_HOST = old_USE_X_FORWARDED_HOST + @override_settings(DEBUG=True, ALLOWED_HOSTS=[]) + def test_host_validation_disabled_in_debug_mode(self): + """If ALLOWED_HOSTS is empty and DEBUG is True, all hosts pass.""" + request = HttpRequest() + request.META = { + 'HTTP_HOST': 'example.com', + } + self.assertEqual(request.get_host(), 'example.com') def test_near_expiration(self): "Cookie will expire when an near expiration time is provided" |
