At some point in your Django hosting journey you are bound to encounter the Django allowed_hosts question. We have a deeper look in this post.
Thomas Bailey
Marketing
Django both provides and uses functions to create fully qualified URLs based on the incoming web request.
Depending on the server configuration, Django makes use of the host header that is sent by the browser on the request to create the fully qualified URL.
The host header is arbitrary and sent on behalf of the user by the browser. Typically, the host header is used when using Apache virtual hosts or Nginx server blocks to determine which application should handle the request where many applications could be sharing the same server IP address.
An issue arises where the host header is used without being checked leading to a potential risk of page resources being loaded from an entirely different domain. For example, a request could be structured to include and malicious host header.
GET /index.html HTTP/1.1
Host: evildomain.com
GET /index.html HTTP/1.1
Host: evildomain.com
The application itself doesn't know about the environment so whilst it might be the only application running, it still makes use of the host header to know if it should handle the request.
<script src="{{ request.META.HTTP_HOST }}/evilscript.js">
The potentially malicious host header could then rendered back to the user to side-load from a malicious site.
By itself, this is limited to the person requesting the file but could then be used to pass on the now malicious page through other attack vectors.
A common misunderstanding is that CORS (cross-origin resource sharing) would provide some protection in this scenario but since the link is static and not loaded through an XMLHTTPRequest, CORS has no bearing upon it.
Once the user has requested the file, a caching proxy or a CDN in front of the site could then store a copy of the page which would now include the malicious request. In turn, users requesting the same piece of content would be served up the poisoned and cached copy of the content.
If the web cache is poisoned, it then becomes possible for the malicious domain to intercept legitimate traffic. Typically, a password reset link mail will include a one-time token and a link back to the originating site for the actual reset to take place. The malicious link could then be used to grab the token, alert the attacker who could then perform the reset on behalf of the user on the legitimate site.
https://{{ request.META.HTTP_HOST }}/password-reset?token=<secret token>&email=victim@friendlydomain.com
Django addresses this through the get_host()
method of django.http.HttpRequest
. This method validates the requested host header against the hosts listed in the ALLOWED_HOSTS settings. If the host does not match then a SuspiciousOperation exception will be thrown.
Note that if your code uses the host header from request.META
directly then ALLOWED_HOSTS
will not be checked and you will not have any validation.
Projects created through the Divio Control Panel make use of the aldryn-django package which includes opinionated settings and configuration settings.
One such configuration change is the automatic setting of allowed_hosts which is based upon your configuration in Divio Control Panel. Changes made to domains, aliases or re-directs will be used to update the allowed_hosts setting.
Note that you must still always check and ensure your allowed_hosts setting is up-to-date and correct and that your application is kept secure for your users.
Stay up to date! Connect with us on LinkedIn and X/Twitter to get exclusive insights and be the first to discover our latest blog posts.
Experience Divio's Open Cloud with our 30-day Free Trial!
Easily deploy your web applications and explore customized solutions.
Sign up now!