El encabezado HTTP_HOST no válido de SuspiciousOperation de Django

95

Después de actualizar a Django 1.5, comencé a recibir errores como este:

Traceback (most recent call last):

File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 92, in get_response
response = middleware_method(request)

File "/usr/local/lib/python2.7/dist-packages/django/middleware/common.py", line 57, in process_request
host = request.get_host()

File "/usr/local/lib/python2.7/dist-packages/django/http/request.py", line 72, in get_host
"Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)

SuspiciousOperation: Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): www.google.com

<WSGIRequest
path:/,
GET:<QueryDict: {}>,
POST:<QueryDict: {}>,
COOKIES:{},
META:{'CONTENT_LENGTH': '',
'CONTENT_TYPE': '',
'DOCUMENT_ROOT': '/etc/nginx/html',
'HTTP_ACCEPT': 'text/html',
'HTTP_HOST': 'www.google.com',
'HTTP_PROXY_CONNECTION': 'close',
'HTTP_USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
'PATH_INFO': u'/',
'QUERY_STRING': '',
'REMOTE_ADDR': '210.245.91.104',
'REMOTE_PORT': '49347',
'REQUEST_METHOD': 'GET',
'REQUEST_URI': '/',
u'SCRIPT_NAME': u'',
'SERVER_NAME': 'www.derekkwok.net',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.0',
'uwsgi.node': 'derekkwok',
'uwsgi.version': '1.4.4',
'wsgi.errors': <open file 'wsgi_errors', mode 'w' at 0xb6d99c28>,
'wsgi.file_wrapper': <built-in function uwsgi_sendfile>,
'wsgi.input': <uwsgi._Input object at 0x953e698>,
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)}>

Lo configuré ALLOWED_HOSTS = ['.derekkwok.net'] en mi archivo settings.py.

¿Que esta pasando aqui? ¿Alguien se hace pasar por Google y accede a mi sitio? ¿O es un caso benigno de alguien que configura incorrectamente su encabezado HTTP_HOST?

Derek Kwok
fuente
¿Descubriste cómo solucionar esto? Frente al mismo problema. Registrando alrededor de un centenar de estos errores todos los días. No tengo idea de si es algo de lo que deba preocuparme.
blinduck
3
Esta publicación de blog proporciona una buena manera de detener los correos electrónicos: tiwoc.de/blog/2013/03/…
Derek Kwok

Respuestas:

64

Si ALLOWED_HOSTSestá configurado correctamente, entonces es posible que alguien esté investigando su sitio en busca de la vulnerabilidad falsificando el encabezado.

Actualmente, los desarrolladores de Django están discutiendo para cambiar esto de un error interno del servidor 500 a una respuesta 400. Vea este boleto .

Brian Neal
fuente
1
Creo que una explicación más probable es que los rastreadores web (robots) simplemente rastrean direcciones IP públicas en el puerto 80, en cuyo caso querría permitirlas.
markmnl
16
@markmnl Un rastreador web legítimo no debería falsificar encabezados de host.
Brian Neal
1
Simplemente se conecta usando la dirección IP, no el nombre de dominio y la dirección IP no está en ALLOWED_HOSTS, o al menos eso es lo que me estaba sucediendo, podría reproducirlo apuntando mi navegador a la dirección IP.
markmnl
Sí. Y en cualquier sitio medio ocupado, esto sucede todo el día todos los días. Lo han arreglado ahora, pero aquí hay una aplicación "desplegable" que lo clasifica en todas las versiones junto con un filtro de tasa de error. github.com/litchfield/django-safelogging
s29
Después de implementar mi sitio web en Internet. Encontré que mucha gente está intentando acceder a mi sitio web utilizando un host no válido. No solo usando la dirección IP. Creo que pueden ser algunas personas que intentan encontrar un sitio web que no puede defender un ataque csrf.
ramwin
130

Si está utilizando Nginx para reenviar solicitudes a Django que se ejecuta en Gunicorn / Apache / uWSGI, puede usar lo siguiente para bloquear solicitudes incorrectas. Gracias a @PaulM por la sugerencia y esta publicación de blog por un ejemplo.

upstream app_server {
    server unix:/tmp/gunicorn_mydomain.com.sock fail_timeout=0;
}

server {

    ...

    ## Deny illegal Host headers
    if ($host !~* ^(mydomain.com|www.mydomain.com)$ ) {
        return 444;
    }

    location  / {
        proxy_pass               http://app_server;
        ...
    }

}
Brent O'Connor
fuente
7
Sería excelente ver esto como una mejora de la sugerencia de los documentos :)
Paul McMillan
1
@webjunkie, Desde su enlace, "Hay casos en los que simplemente no puede evitar el uso de un if, por ejemplo, si necesita probar una variable que no tiene una directiva equivalente". Mi ejemplo lo usa correctamente y funciona bien en mi entorno de producción. Entonces, en conclusión, ¡HAZLO así! :)
Brent O'Connor
2
Bueno, puede evitarlo fácilmente: simplemente especifique solo el nombre_servidor que necesita y deje que el resto sea manejado por un controlador de servidor predeterminado.
webjunkie
1
Vea esta respuesta para una configuración de Apache similar: stackoverflow.com/a/18792080
Denilson Sá Maia
1
Desde el enlace proporcionado por webjunkie: "Directiva si tiene problemas cuando se utiliza en el contexto de la ubicación". El ejemplo dado por Brent usa el ifinterior del serverbloque y no en el locationbloque. ¿Eso significa que ifestá bien en este caso?
Brian Buck
31

Al usar Nginx, puede configurar sus servidores de una manera que solo solicite a los hosts que desea que lleguen a Django en primer lugar. Eso ya no debería generar errores de SuspiciousOperation.

server {
    # default server

    listen 80;
    server_name _ default;

    return 444;
}
server {
    # redirects

    listen 80;
    server_name example.com old.stuff.example.com;

    return 301 http://www.example.com$request_uri;
}
server {
    # app

    listen 80;
    server_name www.example.com; # only hosts in ALLOWED_HOSTS here

    location  / {
        # ...
    }
    # ... your config/proxy stuff
}
webjunkie
fuente
2
Me gusta este enfoque sobre el uso del ifenfoque sugerido por Brent, pero no puedo hacer que funcione con el puerto 443. Intenté imitar su sugerencia (con el puerto de escucha cambiado), y mi sitio SSL real no se carga. es capturado por esta entrada que agregué. Alguna idea sobre como arreglarlo?
Dolan Antenucci
1
Otro cartel en ServerFault.com tenía problemas similares, así que seguí su recomendación sobre el enfoque de declaración if para tráfico 443 únicamente
Dolan Antenucci
1
Parece que debe especificar la ruta a los archivos de certificado si también desea capturar solicitudes SSL (aunque solo desee descartar): server { listen 80 default_server; listen 443; server_name _; ssl_certificate /path/to/file.crt; ssl_certificate_key /path/to/file.key; return 444; }
n__o
¿Qué devolverá Nginx si el HOST de la solicitud no es válido? 50x o 40x?
laike9m
¿Qué es el extra en esta configuración? Tengo el nombre del servidor configurado tanto en las redirecciones como en la sección de la aplicación, todavía obtengo Invalid HTTP_HOST header(con Django 1.8.x)
Csaba Toth
16

Esto se corrige en las versiones más recientes de Django, pero si está usando una versión afectada (por ejemplo, 1.5), puede agregar un filtro a su controlador de registrador para deshacerse de ellos, como se describe en esta publicación de blog.

Revelación:

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

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse',
        },
        # Define filter
        'skip_suspicious_operations': {
            '()': 'django.utils.log.CallbackFilter',
            'callback': skip_suspicious_operations,
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            # Add filter to list of filters
            'filters': ['require_debug_false', 'skip_suspicious_operations'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}
Mgalgs
fuente
1
¿Algún enlace para la corrección o versión donde se implementó? Thx
Marc
1
Lo tenía en la versión 2.0.5
mehmet
Esto no está arreglado en las versiones más recientes de Django. Estoy usando Django 2.0.10
javidazac