티스토리 수익 글 보기

티스토리 수익 글 보기

[1.8.x] Fixed CVE-2016-9014 — Validated Host header when DEBUG=True. · django/django@c401ae9 · GitHub
Skip to content

Commit c401ae9

Browse files
committed
[1.8.x] Fixed CVE-2016-9014 — Validated Host header when DEBUG=True.
This is a security fix.
1 parent 70f9995 commit c401ae9

File tree

4 files changed

+49
21
lines changed

4 files changed

+49
21
lines changed

django/http/request.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,13 @@ def get_host(self):
8585
if server_port != ('443' if self.is_secure() else '80'):
8686
host = '%s:%s' % (host, server_port)
8787

88-
# There is no hostname validation when DEBUG=True
89-
if settings.DEBUG:
90-
return host
88+
# Allow variants of localhost if ALLOWED_HOSTS is empty and DEBUG=True.
89+
allowed_hosts = settings.ALLOWED_HOSTS
90+
if settings.DEBUG and not allowed_hosts:
91+
allowed_hosts = ['localhost', '127.0.0.1', '[::1]']
9192

9293
domain, port = split_domain_port(host)
93-
if domain and validate_host(domain, settings.ALLOWED_HOSTS):
94+
if domain and validate_host(domain, allowed_hosts):
9495
return host
9596
else:
9697
msg = "Invalid HTTP_HOST header: %r." % host

docs/ref/settings.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,18 @@ If the ``Host`` header (or ``X-Forwarded-Host`` if
108108
list, the :meth:`django.http.HttpRequest.get_host()` method will raise
109109
:exc:`~django.core.exceptions.SuspiciousOperation`.
110110

111-
When :setting:`DEBUG` is ``True`` or when running tests, host validation is
112-
disabled; any host will be accepted. Thus it's usually only necessary to set it
113-
in production.
111+
When :setting:`DEBUG` is ``True`` and ``ALLOWED_HOSTS`` is empty, the host
112+
is validated against ``['localhost', '127.0.0.1', '[::1]']``.
114113

115114
This validation only applies via :meth:`~django.http.HttpRequest.get_host()`;
116115
if your code accesses the ``Host`` header directly from ``request.META`` you
117116
are bypassing this security protection.
118117

118+
.. versionchanged:: 1.8.16
119+
120+
In older versions, ``ALLOWED_HOSTS`` wasn't checked if ``DEBUG=True``, but
121+
it's now checked to prevent a DNS rebinding attack.
122+
119123
.. setting:: ALLOWED_INCLUDE_ROOTS
120124

121125
ALLOWED_INCLUDE_ROOTS

docs/releases/1.8.16.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,25 @@ the ``manage.py test --keepdb`` option or if the user has an active session
1919
(such as an attacker's connection).
2020

2121
A randomly generated password is now used for each test run.
22+
23+
DNS rebinding vulnerability when ``DEBUG=True``
24+
===============================================
25+
26+
Older versions of Django don't validate the ``Host`` header against
27+
``settings.ALLOWED_HOSTS`` when ``settings.DEBUG=True``. This makes them
28+
vulnerable to a `DNS rebinding attack
29+
<http://benmmurphy.github.io/blog/2016/07/11/rails-webconsole-dns-rebinding/>`_.
30+
31+
While Django doesn't ship a module that allows remote code execution, this is
32+
at least a cross-site scripting vector, which could be quite serious if
33+
developers load a copy of the production database in development or connect to
34+
some production services for which there's no development instance, for
35+
example. If a project uses a package like the ``django-debug-toolbar``, then
36+
the attacker could execute arbitrary SQL, which could be especially bad if the
37+
developers connect to the database with a superuser account.
38+
39+
``settings.ALLOWED_HOSTS`` is now validated regardless of ``DEBUG``. For
40+
convenience, if ``ALLOWED_HOSTS`` is empty and ``DEBUG=True``, the following
41+
variations of localhost are allowed ``['localhost', '127.0.0.1', '::1']``. If
42+
your local settings file has your production ``ALLOWED_HOSTS`` value, you must
43+
now omit it to get those fallback values.

tests/requests/tests.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -673,21 +673,22 @@ def test_http_get_host_with_x_forwarded_host(self):
673673
request.get_host()
674674

675675
@override_settings(DEBUG=True, ALLOWED_HOSTS=[])
676-
def test_host_validation_disabled_in_debug_mode(self):
677-
"""If ALLOWED_HOSTS is empty and DEBUG is True, all hosts pass."""
678-
request = HttpRequest()
679-
request.META = {
680-
'HTTP_HOST': 'example.com',
681-
}
682-
self.assertEqual(request.get_host(), 'example.com')
676+
def test_host_validation_in_debug_mode(self):
677+
"""
678+
If ALLOWED_HOSTS is empty and DEBUG is True, variants of localhost are
679+
allowed.
680+
"""
681+
valid_hosts = ['localhost', '127.0.0.1', '[::1]']
682+
for host in valid_hosts:
683+
request = HttpRequest()
684+
request.META = {'HTTP_HOST': host}
685+
self.assertEqual(request.get_host(), host)
683686

684-
# Invalid hostnames would normally raise a SuspiciousOperation,
685-
# but we have DEBUG=True, so this check is disabled.
686-
request = HttpRequest()
687-
request.META = {
688-
'HTTP_HOST': "invalid_hostname.com",
689-
}
690-
self.assertEqual(request.get_host(), "invalid_hostname.com")
687+
# Other hostnames raise a SuspiciousOperation.
688+
with self.assertRaises(SuspiciousOperation):
689+
request = HttpRequest()
690+
request.META = {'HTTP_HOST': 'example.com'}
691+
request.get_host()
691692

692693
@override_settings(ALLOWED_HOSTS=[])
693694
def test_get_host_suggestion_of_allowed_host(self):

0 commit comments

Comments
 (0)