Loading django/http/request.py +2 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ from django.utils.encoding import force_bytes, force_text, force_str, iri_to_uri RAISE_ERROR = object() absolute_http_url_re = re.compile(r"^https?://", re.I) host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$") class UnreadablePostError(IOError): Loading Loading @@ -64,7 +65,7 @@ class HttpRequest(object): host = '%s:%s' % (host, server_port) # Disallow potentially poisoned hostnames. if set(';/?@&=+$,').intersection(host): if not host_validation_re.match(host.lower()): raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host) return host Loading docs/topics/security.txt +25 −0 Original line number Diff line number Diff line Loading @@ -185,6 +185,31 @@ recommend you ensure your Web server is configured such that: 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 .. _additional-security-topics: Additional security topics Loading tests/regressiontests/requests/tests.py +7 −3 Original line number Diff line number Diff line Loading @@ -116,13 +116,15 @@ class RequestsTests(unittest.TestCase): '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:someone@somestie.com:80', 'example.com:80/badpath' 'example.com:dr.frankenstein@evil.tld:80', 'example.com:80/badpath', 'example.com: recovermypassword.com', ] for host in legit_hosts: Loading Loading @@ -186,13 +188,15 @@ class RequestsTests(unittest.TestCase): '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:80/badpath', 'example.com: recovermypassword.com', ] for host in legit_hosts: Loading Loading
django/http/request.py +2 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ from django.utils.encoding import force_bytes, force_text, force_str, iri_to_uri RAISE_ERROR = object() absolute_http_url_re = re.compile(r"^https?://", re.I) host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$") class UnreadablePostError(IOError): Loading Loading @@ -64,7 +65,7 @@ class HttpRequest(object): host = '%s:%s' % (host, server_port) # Disallow potentially poisoned hostnames. if set(';/?@&=+$,').intersection(host): if not host_validation_re.match(host.lower()): raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host) return host Loading
docs/topics/security.txt +25 −0 Original line number Diff line number Diff line Loading @@ -185,6 +185,31 @@ recommend you ensure your Web server is configured such that: 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 .. _additional-security-topics: Additional security topics Loading
tests/regressiontests/requests/tests.py +7 −3 Original line number Diff line number Diff line Loading @@ -116,13 +116,15 @@ class RequestsTests(unittest.TestCase): '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:someone@somestie.com:80', 'example.com:80/badpath' 'example.com:dr.frankenstein@evil.tld:80', 'example.com:80/badpath', 'example.com: recovermypassword.com', ] for host in legit_hosts: Loading Loading @@ -186,13 +188,15 @@ class RequestsTests(unittest.TestCase): '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:80/badpath', 'example.com: recovermypassword.com', ] for host in legit_hosts: Loading