En-tête HTTP_HOST incorrect de Django's SuspiciousOperation

95

Après la mise à niveau vers Django 1.5, j'ai commencé à recevoir des erreurs comme celle-ci:

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

J'ai défini ALLOWED_HOSTS = ['.derekkwok.net'] dans mon fichier settings.py.

Qu'est-ce qui se passe ici? Est-ce que quelqu'un prétend être Google et accède à mon site? Ou est-ce un cas bénin de quelqu'un qui définit son en-tête HTTP_HOST de manière incorrecte?

Derek Kwok
la source
Avez-vous trouvé comment résoudre ce problème? Face au même problème. Enregistrement d'une centaine de ces erreurs chaque jour. Je ne sais pas si c'est quelque chose dont je dois m'inquiéter.
blinduck
3
Ce billet de blog offre un bon moyen d'arrêter les e-mails: tiwoc.de/blog/2013/03/…
Derek Kwok

Réponses:

64

Si votre ALLOWED_HOSTSest configuré correctement, il est possible que quelqu'un sonde votre site pour la vulnérabilité en usurpant l'en-tête.

Il y a actuellement des discussions entre les développeurs de Django pour changer cela d'une erreur de serveur interne 500 à une réponse 400. Voir ce ticket .

Brian Neal
la source
1
Je pense qu'une explication plus probable est que les robots d'exploration Web (robots) explorent simplement les adresses IP publiques sur le port 80 - auquel cas vous voudrez les autoriser.
markmnl
16
@markmnl Un robot d'exploration Web légitime ne doit pas falsifier les en-têtes d'hôte.
Brian Neal
1
Il s'agit simplement de se connecter en utilisant l'adresse IP et non le nom de domaine et l'adresse IP n'est pas dans ALLOWED_HOSTS - ou du moins c'est ce qui se passait avec moi - je pourrais le reprocher en pointant mon navigateur sur l'adresse IP.
markmnl
Oui. Et dans n'importe quel site à moitié occupé, cela se produit toute la journée, tous les jours. Ils ont résolu le problème maintenant, mais voici une application "drop-in" qui le trie sur toutes les versions avec un filtre de taux d'erreur. github.com/litchfield/django-safelogging
s29
Après avoir déployé mon site Web sur Internet. J'ai trouvé que beaucoup de gens essayaient d'accéder à mon site Web en utilisant un hôte invalide. Non seulement en utilisant l'adresse IP. Je pense que certaines personnes essaient peut-être de trouver un site Web qui ne peut pas défendre une attaque csrf.
ramwin
130

Si vous utilisez Nginx pour transférer des requêtes à Django fonctionnant sur Gunicorn / Apache / uWSGI, vous pouvez utiliser ce qui suit pour bloquer les mauvaises requêtes. Merci à @PaulM pour la suggestion et cet article de blog pour un exemple.

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
la source
7
Ce serait excellent de voir cela comme une amélioration de l' indice de la documentation :)
Paul McMillan
1
@webjunkie, De votre lien, "Il y a des cas où vous ne pouvez tout simplement pas éviter d'utiliser un if, par exemple si vous devez tester une variable qui n'a pas de directive équivalente." Mon exemple l'utilise correctement et fonctionne bien dans mon environnement de production. Alors en conclusion, faites-le comme ça! :)
Brent O'Connor
2
Eh bien, vous pouvez facilement l'éviter: spécifiez simplement le nom du serveur dont vous avez besoin et laissez le reste être géré par un gestionnaire de serveur par défaut.
webjunkie
1
Voir cette réponse pour une configuration Apache similaire: stackoverflow.com/a/18792080
Denilson Sá Maia
1
À partir du lien fourni par webjunkie: "Directive si elle a des problèmes lorsqu'elle est utilisée dans un contexte de localisation". L'exemple donné par Brent utilise l' ifintérieur du serverbloc et non dans le locationbloc. Cela signifie-t-il que ça ifva dans ce cas?
brian buck
31

Lorsque vous utilisez Nginx, vous pouvez configurer vos serveurs de manière à ne demander que les hôtes que vous souhaitez accéder à Django en premier lieu. Cela ne devrait plus vous donner d'erreurs 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
la source
2
J'aime cette approche sur l'utilisation de l' ifapproche suggérée par Brent, mais je ne peux pas la faire fonctionner avec le port 443. J'ai essayé d'imiter votre suggestion (avec le port d'écoute modifié), et mon site SSL actuel ne se charge pas - il est capturé par cette entrée que j'ai ajoutée. Des idées sur comment réparer?
Dolan Antenucci
1
Une autre affiche sur ServerFault.com avait des problèmes similaires, j'ai donc suivi sa recommandation sur l'approche de l'instruction if pour le trafic 443 uniquement
Dolan Antenucci
1
Il semble que vous deviez spécifier le chemin d'accès aux fichiers de certificat si vous souhaitez également intercepter les requêtes SSL (même si vous souhaitez simplement les rejeter): 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
Que retournera Nginx si l'hôte de la demande n'est pas valide? 50x ou 40x?
laike9m
Quel est le supplément dans cette configuration? J'ai le nom du serveur défini à la fois dans les redirections et dans la section de l'application, je reçois toujours Invalid HTTP_HOST header(avec Django 1.8.x)
Csaba Toth
16

Ceci est corrigé dans les versions plus récentes de Django, mais si vous utilisez une version affectée (par exemple 1.5), vous pouvez ajouter un filtre à votre gestionnaire de journalisation pour vous en débarrasser, comme indiqué dans ce billet de blog.

Divulgacher:

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
la source
1
Un lien pour le correctif ou la version implémentée? Thx
Marc
1
Je l'ai eu sur la version 2.0.5
mehmet
Ce n'est pas corrigé sur les versions plus récentes de Django. J'utilise Django 2.0.10
javidazac