Django 1.4.4 introduced the ALLOWED_HOSTS setting as implemented in django/http/__init__.py:
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.
If the host header holds an unknown host and DEBUG is set to False, a SuspiciousOperation exception is raised. This results in an HTTP 500 (Internal Server Error) error code which is returned to the client. I believe this was chosen over the HTTP 4xy-class error (Client Error) so that the admins are notified via email (see the Error Reporting docs). This is a good thing if you have misconfigured the ALLOWED_HOSTS setting and forgot to include some host name that should be usable with the site.
If, however, you are constantly spammed by Django error messages because someone is scanning your website and tries to set a fake Host header, things get annoying. I posted a bug report on the Django bug tracker, and it looks like this will be handled either in 1.5.1 or at least in 1.6. (I wish to note at this place that I’m very grateful that Django has such a responsive dev team. The first response came in in less than 3 hours after the bug, and the first proposed patch was posted on the same day. Thank you!)
Until Django is properly fixed, I need some workaround that I implemented as a logging filter that prevents SuspiciousOperation exceptions from being sent via email (it does not change the HTTP 500 into an HTTP 400):
from django.core.exceptions import SuspiciousOperation def skip_suspicious_operations(record): if record.exc_info: exc_value = record.exc_info[1] if isinstance(exc_value, SuspiciousOperation): return False return True
To activate this filter, it must be included in your settings.py file just like in the logging docs, where I also got the blueprint for the code that is listed above.
I prepared a minimal example project for your convenience. It comes with the filter enabled.
If you’d like to verify that the filter works, do the following:
- Check out the example project and make sure that Django is installed (either globally or in a virtualenv).
- Run the dev server:
python manage.py runserver
- On a second terminal, run the SMTP debugging server built into Python:
sudo python -m smtpd -n -c DebuggingServer localhost:25
(sudo is necessary because port 25 (SMTP default) can only be used by root)
- On a third terminal, check that the main page works:
curl http://localhost:8000/
(should print “Hello, world” to the console)
- Check
that changing the Host name leads to an error message, but that no
email is sent (look at the terminal that runs the SMTP server, nothing
should be printed there):
curl -H "Host: asdfasdf" http://localhost:8000/
- Check that other server errors are sent out as an email (the SMTP debugging server should print the lenghty message):
curl http://localhost:8000/500