Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[1.4.x] Added ALLOWED_HOSTS setting for HTTP host header validation.

This is a security fix; disclosure and advisory coming shortly.
  • Loading branch information...
commit 9936fdb11d0bbf0bd242f259bfb97bbf849d16f8 1 parent 57b62a7
@carljm carljm authored
View
4 django/conf/global_settings.py
@@ -29,6 +29,10 @@
# * Receive x-headers
INTERNAL_IPS = ()
+# Hosts/domain names that are valid for this site.
+# "*" matches anything, ".example.com" matches example.com and all subdomains
+ALLOWED_HOSTS = ['*']
+
# Local time zone for this installation. All choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
# systems may support all possibilities). When USE_TZ is True, this is
View
4 django/conf/project_template/project_name/settings.py
@@ -20,6 +20,10 @@
}
}
+# Hosts/domain names that are valid for this site; required if DEBUG is False
+# See https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#allowed-hosts
+ALLOWED_HOSTS = []
+
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
View
1  django/contrib/auth/tests/views.py
@@ -107,6 +107,7 @@ def test_email_found_custom_from(self):
self.assertEqual(len(mail.outbox), 1)
self.assertEqual("staffmember@example.com", mail.outbox[0].from_email)
+ @override_settings(ALLOWED_HOSTS=['adminsite.com'])
def test_admin_reset(self):
"If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override."
response = self.client.post('/admin_password_reset/',
View
2  django/contrib/contenttypes/tests.py
@@ -9,6 +9,7 @@
from django.http import HttpRequest, Http404
from django.test import TestCase
from django.utils.encoding import smart_str
+from django.test.utils import override_settings
class FooWithoutUrl(models.Model):
@@ -114,6 +115,7 @@ def test_get_for_models_full_cache(self):
FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
})
+ @override_settings(ALLOWED_HOSTS=['example.com'])
def test_shortcut_view(self):
"""
Check that the shortcut view (used for the admin "view on site"
View
2  django/contrib/sites/tests.py
@@ -3,6 +3,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest
from django.test import TestCase
+from django.test.utils import override_settings
class SitesFrameworkTests(TestCase):
@@ -39,6 +40,7 @@ def test_site_cache(self):
site = Site.objects.get_current()
self.assertEqual(u"Example site", site.name)
+ @override_settings(ALLOWED_HOSTS=['example.com'])
def test_get_current_site(self):
# Test that the correct Site object is returned
request = HttpRequest()
View
51 django/http/__init__.py
@@ -215,11 +215,12 @@ def get_host(self):
if server_port != (self.is_secure() and '443' or '80'):
host = '%s:%s' % (host, server_port)
- # Disallow potentially poisoned hostnames.
- if not host_validation_re.match(host.lower()):
- raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host)
-
- return host
+ allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS
+ if validate_host(host, allowed_hosts):
+ return host
+ else:
+ raise SuspiciousOperation(
+ "Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)
def get_full_path(self):
# RFC 3986 requires query string arguments to be in the ASCII range.
@@ -799,3 +800,43 @@ def str_to_unicode(s, encoding):
else:
return s
+def validate_host(host, allowed_hosts):
+ """
+ Validate the given host header value for this site.
+
+ Check that the host looks valid and matches a host or host pattern in the
+ given list of ``allowed_hosts``. Any pattern beginning with a period
+ matches a domain and all its subdomains (e.g. ``.example.com`` matches
+ ``example.com`` and any subdomain), ``*`` matches anything, and anything
+ else must match exactly.
+
+ Return ``True`` for a valid host, ``False`` otherwise.
+
+ """
+ # All validation is case-insensitive
+ host = host.lower()
+
+ # Basic sanity check
+ if not host_validation_re.match(host):
+ return False
+
+ # Validate only the domain part.
+ if host[-1] == ']':
+ # It's an IPv6 address without a port.
+ domain = host
+ else:
+ domain = host.rsplit(':', 1)[0]
+
+ for pattern in allowed_hosts:
+ pattern = pattern.lower()
+ match = (
+ pattern == '*' or
+ pattern.startswith('.') and (
+ domain.endswith(pattern) or domain == pattern[1:]
+ ) or
+ pattern == domain
+ )
+ if match:
+ return True
+
+ return False
View
6 django/test/utils.py
@@ -75,6 +75,9 @@ def setup_test_environment():
mail.original_email_backend = settings.EMAIL_BACKEND
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
+ settings._original_allowed_hosts = settings.ALLOWED_HOSTS
+ settings.ALLOWED_HOSTS = ['*']
+
mail.outbox = []
deactivate()
@@ -93,6 +96,9 @@ def teardown_test_environment():
settings.EMAIL_BACKEND = mail.original_email_backend
del mail.original_email_backend
+ settings.ALLOWED_HOSTS = settings._original_allowed_hosts
+ del settings._original_allowed_hosts
+
del mail.outbox
View
36 docs/ref/settings.txt
@@ -68,6 +68,42 @@ of (Full name, email address). Example::
Note that Django will email *all* of these people whenever an error happens.
See :doc:`/howto/error-reporting` for more information.
+.. setting:: ALLOWED_HOSTS
+
+ALLOWED_HOSTS
+-------------
+
+Default: ``['*']``
+
+A list of strings representing the host/domain names that this Django site can
+serve. This is a security measure to prevent an attacker from poisoning caches
+and password reset emails with links to malicious hosts by submitting requests
+with a fake HTTP ``Host`` header, which is possible even under many
+seemingly-safe webserver configurations.
+
+Values in this list can be fully qualified names (e.g. ``'www.example.com'``),
+in which case they will be matched against the request's ``Host`` header
+exactly (case-insensitive, not including port). A value beginning with a period
+can be used as a subdomain wildcard: ``'.example.com'`` will match
+``example.com``, ``www.example.com``, and any other subdomain of
+``example.com``. A value of ``'*'`` will match anything; in this case you are
+responsible to provide your own validation of the ``Host`` header (perhaps in a
+middleware; if so this middleware must be listed first in
+:setting:`MIDDLEWARE_CLASSES`).
+
+If the ``Host`` header (or ``X-Forwarded-Host`` if
+:setting:`USE_X_FORWARDED_HOST` is enabled) does not match any value in this
+list, the :meth:`django.http.HttpRequest.get_host()` method will raise
+:exc:`~django.core.exceptions.SuspiciousOperation`.
+
+When :setting:`DEBUG` is ``True`` or when running tests, host validation is
+disabled; any host will be accepted. Thus it's usually only necessary to set it
+in production.
+
+This validation only applies via :meth:`~django.http.HttpRequest.get_host()`;
+if your code accesses the ``Host`` header directly from ``request.META`` you
+are bypassing this security protection.
+
.. setting:: ALLOWED_INCLUDE_ROOTS
ALLOWED_INCLUDE_ROOTS
View
39 docs/releases/1.4.4.txt
@@ -0,0 +1,39 @@
+==========================
+Django 1.4.4 release notes
+==========================
+
+*February 19, 2013*
+
+This is the fourth bugfix/security release in the Django 1.4 series.
+
+Host header poisoning
+---------------------
+
+Some parts of Django -- independent of end-user-written applications -- make
+use of full URLs, including domain name, which are generated from the HTTP Host
+header. Django's documentation has for some time contained notes advising users
+on how to configure webservers to ensure that only valid Host headers can reach
+the Django application. However, it has been reported to us that even with the
+recommended webserver configurations there are still techniques available for
+tricking many common webservers into supplying the application with an
+incorrect and possibly malicious Host header.
+
+For this reason, Django 1.4.4 adds a new setting, ``ALLOWED_HOSTS``, containing
+an explicit list of valid host/domain names for this site. A request with a
+Host header not matching an entry in this list will raise
+``SuspiciousOperation`` if ``request.get_host()`` is called. For full details
+see the documentation for the :setting:`ALLOWED_HOSTS` setting.
+
+The default value for this setting in Django 1.4.4 is `['*']` (matching any
+host), for backwards-compatibility, but we strongly encourage all sites to set
+a more restrictive value.
+
+This host validation is disabled when ``DEBUG`` is ``True`` or when running tests.
+
+
+Other bugfixes and changes
+==========================
+
+* Changed a SQL command syntax to be MySQL 4 compatible (#19702).
+* Added backwards-compatibility with old unsalted MD5 passwords (#18144).
+* Numerous documentation improvements and fixes.
View
1  docs/releases/index.txt
@@ -20,6 +20,7 @@ Final releases
.. toctree::
:maxdepth: 1
+ 1.4.4
1.4.2
1.4.1
1.4
View
68 docs/topics/security.txt
@@ -149,48 +149,40 @@ server, there are some additional steps you may need:
.. _additional-security-topics:
-Host headers and virtual hosting
-================================
+Host header validation
+======================
-Django uses the ``Host`` header provided by the client to construct URLs
-in certain cases. While these values are sanitized to prevent Cross
-Site Scripting attacks, they can be used for Cross-Site Request
-Forgery and cache poisoning attacks in some circumstances. We
-recommend you ensure your Web server is configured such that:
+Django uses the ``Host`` header provided by the client to construct URLs in
+certain cases. While these values are sanitized to prevent Cross Site Scripting
+attacks, a fake ``Host`` value can be used for Cross-Site Request Forgery,
+cache poisoning attacks, and poisoning links in emails.
- * It always validates incoming HTTP ``Host`` headers against the expected
- host name.
- * Disallows requests with no ``Host`` header.
- * Is *not* configured with a catch-all virtual host that forwards requests
- to a Django application.
+Because even seemingly-secure webserver configurations are susceptible to fake
+``Host`` headers, Django validates ``Host`` headers against the
+:setting:`ALLOWED_HOSTS` setting in the
+:meth:`django.http.HttpRequest.get_host()` method.
-Additionally, as of 1.3.1, Django requires you to explicitly enable support for
-the ``X-Forwarded-Host`` header if your configuration requires it.
-
-Configuration for Apache
-------------------------
-
-The easiest way to get the described behavior in Apache is as follows. Create
-a `virtual host`_ using the ServerName_ and ServerAlias_ directives to restrict
-the domains Apache reacts to. Please keep in mind that while the directives do
-support ports the match is only performed against the hostname. This means that
-the ``Host`` header could still contain a port pointing to another webserver on
-the same machine. The next step is to make sure that your newly created virtual
-host is not also the default virtual host. Apache uses the first virtual host
-found in the configuration file as default virtual host. As such you have to
-ensure that you have another virtual host which will act as catch-all virtual
-host. Just add one if you do not have one already, there is nothing special
-about it aside from ensuring it is the first virtual host in the configuration
-file. Debian/Ubuntu users usually don't have to take any action, since Apache
-ships with a default virtual host in ``sites-available`` which is linked into
-``sites-enabled`` as ``000-default`` and included from ``apache2.conf``. Just
-make sure not to name your site ``000-abc``, since files are included in
-alphabetical order.
-
-.. _virtual host: http://httpd.apache.org/docs/2.2/vhosts/
-.. _ServerName: http://httpd.apache.org/docs/2.2/mod/core.html#servername
-.. _ServerAlias: http://httpd.apache.org/docs/2.2/mod/core.html#serveralias
+This validation only applies via :meth:`~django.http.HttpRequest.get_host()`;
+if your code accesses the ``Host`` header directly from ``request.META`` you
+are bypassing this security protection.
+
+For more details see the full :setting:`ALLOWED_HOSTS` documentation.
+
+.. warning::
+ Previous versions of this document recommended configuring your webserver to
+ ensure it validates incoming HTTP ``Host`` headers. While this is still
+ recommended, in many common webservers a configuration that seems to
+ validate the ``Host`` header may not in fact do so. For instance, even if
+ Apache is configured such that your Django site is served from a non-default
+ virtual host with the ``ServerName`` set, it is still possible for an HTTP
+ request to match this virtual host and supply a fake ``Host`` header. Thus,
+ Django now requires that you set :setting:`ALLOWED_HOSTS` explicitly rather
+ than relying on webserver configuration.
+
+Additionally, as of 1.3.1, Django requires you to explicitly enable support for
+the ``X-Forwarded-Host`` header (via the :setting:`USE_X_FORWARDED_HOST`
+setting) if your configuration requires it.
View
4 tests/regressiontests/csrf_tests/tests.py
@@ -7,6 +7,7 @@
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 @@ def test_token_node_with_new_csrf_cookie(self):
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 @@ def test_https_bad_referer(self):
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 @@ def test_https_good_referer(self):
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
View
271 tests/regressiontests/requests/tests.py
@@ -14,7 +14,7 @@
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 @@ def test_httprequest_location(self):
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 = {
- 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 = {
- 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 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 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 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.
+ # 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',
+ ]
+
+ 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': 80,
+ 'HTTP_HOST': host,
}
- self.assertEqual(request.get_host(), 'internal.com')
+ request.get_host()
- # Check if HTTP_HOST isn't provided, and we're on a nonstandard port
- request = HttpRequest()
- request.META = {
- u'SERVER_NAME': u'internal.com',
- u'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
- ]
-
- 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(USE_X_FORWARDED_HOST=True, ALLOWED_HOSTS=['*'])
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 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 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 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')
+
+ # 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 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.
+ # 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
+ ]
+
+ 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:
request = HttpRequest()
request.META = {
- u'SERVER_NAME': u'internal.com',
- u'SERVER_PORT': 80,
+ 'HTTP_HOST': host,
}
- self.assertEqual(request.get_host(), 'internal.com')
+ request.get_host()
- # Check if HTTP_HOST isn't provided, and we're on a nonstandard port
- request = HttpRequest()
- request.META = {
- u'SERVER_NAME': u'internal.com',
- u'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
- ]
-
- 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"

1 comment on commit 9936fdb

@xiyang1081

It is very good!

Please sign in to comment.
Something went wrong with that request. Please try again.