Skip to content

Commit 319627c

Browse files
committed
[1.4.X] Fixed a security issue in get_host.
Full disclosure and new release forthcoming.
1 parent b2ae0a6 commit 319627c

File tree

3 files changed

+38
-4
lines changed

3 files changed

+38
-4
lines changed

django/http/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ def __init__(self, *args, **kwargs):
126126
RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
127127

128128
absolute_http_url_re = re.compile(r"^https?://", re.I)
129+
host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$")
130+
129131

130132
class Http404(Exception):
131133
pass
@@ -214,7 +216,7 @@ def get_host(self):
214216
host = '%s:%s' % (host, server_port)
215217

216218
# Disallow potentially poisoned hostnames.
217-
if set(';/?@&=+$,').intersection(host):
219+
if not host_validation_re.match(host.lower()):
218220
raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host)
219221

220222
return host

docs/topics/security.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,33 @@ recommend you ensure your Web server is configured such that:
167167
Additionally, as of 1.3.1, Django requires you to explicitly enable support for
168168
the ``X-Forwarded-Host`` header if your configuration requires it.
169169

170+
Configuration for Apache
171+
------------------------
172+
173+
The easiest way to get the described behavior in Apache is as follows. Create
174+
a `virtual host`_ using the ServerName_ and ServerAlias_ directives to restrict
175+
the domains Apache reacts to. Please keep in mind that while the directives do
176+
support ports the match is only performed against the hostname. This means that
177+
the ``Host`` header could still contain a port pointing to another webserver on
178+
the same machine. The next step is to make sure that your newly created virtual
179+
host is not also the default virtual host. Apache uses the first virtual host
180+
found in the configuration file as default virtual host. As such you have to
181+
ensure that you have another virtual host which will act as catch-all virtual
182+
host. Just add one if you do not have one already, there is nothing special
183+
about it aside from ensuring it is the first virtual host in the configuration
184+
file. Debian/Ubuntu users usually don't have to take any action, since Apache
185+
ships with a default virtual host in ``sites-available`` which is linked into
186+
``sites-enabled`` as ``000-default`` and included from ``apache2.conf``. Just
187+
make sure not to name your site ``000-abc``, since files are included in
188+
alphabetical order.
189+
190+
.. _virtual host: http://httpd.apache.org/docs/2.2/vhosts/
191+
.. _ServerName: http://httpd.apache.org/docs/2.2/mod/core.html#servername
192+
.. _ServerAlias: http://httpd.apache.org/docs/2.2/mod/core.html#serveralias
193+
194+
195+
196+
170197
Additional security topics
171198
==========================
172199

tests/regressiontests/requests/tests.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
from __future__ import with_statement
23

34
import time
@@ -154,13 +155,15 @@ def test_http_get_host(self):
154155
'12.34.56.78:443',
155156
'[2001:19f0:feee::dead:beef:cafe]',
156157
'[2001:19f0:feee::dead:beef:cafe]:8080',
158+
'xn--4ca9at.com', # Punnycode for öäü.com
157159
]
158160

159161
poisoned_hosts = [
160162
'example.com@evil.tld',
161163
'example.com:dr.frankenstein@evil.tld',
162-
'example.com:someone@somestie.com:80',
163-
'example.com:80/badpath'
164+
'example.com:dr.frankenstein@evil.tld:80',
165+
'example.com:80/badpath',
166+
'example.com: recovermypassword.com',
164167
]
165168

166169
for host in legit_hosts:
@@ -230,13 +233,15 @@ def test_http_get_host_with_x_forwarded_host(self):
230233
'12.34.56.78:443',
231234
'[2001:19f0:feee::dead:beef:cafe]',
232235
'[2001:19f0:feee::dead:beef:cafe]:8080',
236+
'xn--4ca9at.com', # Punnycode for öäü.com
233237
]
234238

235239
poisoned_hosts = [
236240
'example.com@evil.tld',
237241
'example.com:dr.frankenstein@evil.tld',
238242
'example.com:dr.frankenstein@evil.tld:80',
239-
'example.com:80/badpath'
243+
'example.com:80/badpath',
244+
'example.com: recovermypassword.com',
240245
]
241246

242247
for host in legit_hosts:

0 commit comments

Comments
 (0)