[1.5.x] Added a new required ALLOWED_HOSTS setting for HTTP host header validation.

This is a security fix; disclosure and advisory coming shortly.
This commit is contained in:
Carl Meyer 2013-02-09 10:26:24 -07:00
parent 5d853db90e
commit a7e33c5bf3
12 changed files with 169 additions and 44 deletions

View file

@ -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: ``[]`` (Empty list)
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 file

@ -354,6 +354,16 @@ Backwards incompatible changes in 1.5
deprecation timeline for a given feature, its removal may appear as a
backwards incompatible change.
``ALLOWED_HOSTS`` required in production
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The new :setting:`ALLOWED_HOSTS` setting validates the request's ``Host``
header and protects against host-poisoning attacks. This setting is now
required whenever :setting:`DEBUG` is ``False``, or else
:meth:`django.http.HttpRequest.get_host()` will raise
:exc:`~django.core.exceptions.SuspiciousOperation`. For more details see the
:setting:`full documentation<ALLOWED_HOSTS>` for the new setting.
Managers on abstract models
~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -167,47 +167,40 @@ server, there are some additional steps you may need:
.. _host-headers-virtual-hosting:
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.
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 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
the ``X-Forwarded-Host`` header (via the :setting:`USE_X_FORWARDED_HOST`
setting) if your configuration requires it.
.. _additional-security-topics: