Intestazione HTTP_HOST non valida di SuspiciousOperation di Django


95

Dopo l'aggiornamento a Django 1.5, ho iniziato a ricevere errori come questo:

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)}>

Ho impostato ALLOWED_HOSTS = ['.derekkwok.net'] nel mio file settings.py.

Che cosa sta succedendo qui? È qualcuno che finge di essere Google e accede al mio sito? O è un caso benigno di qualcuno che imposta la propria intestazione HTTP_HOST in modo errato?


Hai capito come risolvere questo problema? Di fronte allo stesso problema. Registrazione di circa un centinaio di questi errori ogni giorno. Non ho idea se è qualcosa di cui devo preoccuparmi.
blinduck

3
Questo post del blog fornisce un bel modo per fermare le e-mail: tiwoc.de/blog/2013/03/…
Derek Kwok

Risposte:


64

Se il tuo ALLOWED_HOSTSè impostato correttamente, è possibile che qualcuno stia sondando il tuo sito per la vulnerabilità falsificando l'intestazione.

In questo momento gli sviluppatori Django stanno discutendo per cambiare questo da un errore interno del server 500 a una risposta 400. Vedi questo biglietto .


1
Penso che una spiegazione più probabile sia che i web crawler (robot) eseguano semplicemente la scansione degli indirizzi IP pubblici sulla porta 80, nel qual caso dovresti consentirli.
markmnl

16
@markmnl Un crawler web legittimo non dovrebbe falsificare le intestazioni host.
Brian Neal

1
Si tratta solo di connettersi utilizzando l'indirizzo IP non il nome di dominio e l'indirizzo IP non è in ALLOWED_HOSTS - o almeno questo è quello che stava succedendo con me - potrei riprogrammarlo puntando il mio browser all'indirizzo IP.
markmnl

Sì. E in qualsiasi sito mezzo occupato, questo accade tutto il giorno tutti i giorni. L'hanno risolto ora, ma ecco un'app "drop-in" che lo ordina in tutte le versioni insieme a un filtro del tasso di errore. github.com/litchfield/django-safelogging
s29

Dopo aver distribuito il mio sito Web su Internet. Ho scoperto che molte persone stanno cercando di accedere al mio sito web utilizzando un host non valido. Non solo utilizzando l'indirizzo IP. Penso che potrebbero essere alcune persone che cercano di trovare un sito Web che non può difendere un attacco CSRF.
ramwin

130

Se stai usando Nginx per inoltrare richieste a Django in esecuzione su Gunicorn / Apache / uWSGI, puoi usare quanto segue per bloccare le richieste errate. Grazie a @PaulM per il suggerimento e questo post sul blog come esempio.

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;
        ...
    }

}

7
Sarebbe eccellente vederlo come un miglioramento del suggerimento dei documenti :)
Paul McMillan

1
@webjunkie, Dal tuo link, "Ci sono casi in cui semplicemente non puoi evitare di usare un if, ad esempio se devi testare una variabile che non ha una direttiva equivalente." Il mio esempio lo utilizza correttamente e funziona bene nel mio ambiente di produzione. Quindi, in conclusione, fallo in questo modo! :)
Brent O'Connor

2
Puoi evitarlo facilmente: specifica solo il nome_server di cui hai bisogno e lascia che il resto sia gestito da un gestore di server predefinito.
webjunkie

1
Vedi questa risposta per una configurazione Apache simile: stackoverflow.com/a/18792080
Denilson Sá Maia,

1
Dal link fornito da webjunkie: "La direttiva se ha problemi se utilizzata nel contesto della posizione". L'esempio fornito da Brent utilizza l' ifinterno del serverblocco e non nel locationblocco. Significa che ifva bene in questo caso?
brian buck

31

Quando usi Nginx puoi configurare i tuoi server in un modo che solo le richieste agli host che vuoi che arrivino a Django in primo luogo. Questo dovrebbe non darti più errori di 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
}

2
Mi piace questo approccio rispetto a quello ifsuggerito da Brent, ma non riesco a farlo funzionare con la porta 443. Ho provato a imitare il tuo suggerimento (con la porta di ascolto modificata) e il mio sito SSL effettivo non si carica - viene catturato da questa voce che ho aggiunto. Delle idee su come riparare?
Dolan Antenucci

1
Un altro poster su ServerFault.com aveva problemi simili, quindi ho seguito la sua raccomandazione sull'approccio if-statement solo per il traffico 443
Dolan Antenucci

1
Sembra che tu debba specificare il percorso dei file di certificato se vuoi catturare anche le richieste SSL (anche se vuoi solo scartare): 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

Cosa restituirà Nginx se l'HOST della richiesta non è valido? 50x o 40x?
laike9m

Qual è l'extra in questa configurazione? Ho il nome del server impostato sia nei reindirizzamenti che nella sezione app, ottengo ancora Invalid HTTP_HOST header(con Django 1.8.x)
Csaba Toth

16

Questo problema è stato risolto nelle versioni più recenti di Django, ma se stai usando una versione interessata (es. 1.5) puoi aggiungere un filtro al gestore del logger per sbarazzartene, come descritto in questo post del blog.

Spoiler:

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,
        },
    }
}

1
Qualche collegamento per la correzione o la versione dove implementato? Thx
Marc

1
L'avevo nella versione 2.0.5
mehmet

Questo non è stato risolto nelle versioni più recenti di Django. Sto usando Django 2.0.10
javidazac
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.