Django: Prevent email notification on SuspiciousOperation

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

9 thoughts on “Django: Prevent email notification on SuspiciousOperation

  1. Pingback: Django Errors Going to Spam | Django Daily

  2. Hi,

    In my case (Django 1.6.2) I still get an email for curl -H “Host: asdfasdf” http://localhost:8000/ .
    Here it is :

    ———- MESSAGE FOLLOWS ———-
    MIME-Version: 1.0
    Content-Type: text/plain; charset=”utf-8″
    Content-Transfer-Encoding: 7bit
    Subject: [Django] ERROR: Invalid HTTP_HOST header: ‘asdfasdf’.You may need to
    add u’asdfasdf’ to ALLOWED_HOSTS.
    From: root@localhost
    To: ad@min.example
    Date: Thu, 13 Mar 2014 11:43:07 -0000
    Message-ID:
    X-Peer: 127.0.0.1

    No stack trace available

    Request repr() unavailable.
    ———— END MESSAGE ————

    • The same problem occurs for Django 1.6 and Django 1.6.1 (tested in virtualenvs).
      I can’t get an other behavior than this “Request repr() unavailable.”… !

      Any idea?

          • I think the fix for Django 1.6 is to change or duplicate ‘django.request’ to ‘django.security’ under LOGGING['loggers'], like so:


            ‘loggers’: {
            ‘django.request’: {
            ‘handlers’: ['mail_admins'],
            ‘level’: ‘ERROR’,
            ‘propagate’: True,
            },
            ‘django.security’: {
            ‘handlers’: ['mail_admins'],
            ‘level’: ‘ERROR’,
            ‘propagate’: True,
            },
            }

            Since these SuspiciousOperation warnings are now routed through the django.security log handler: see the comments on https://code.djangoproject.com/ticket/17101

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>