Django's SuspiciousOperation Invalid HTTP_HOST header

96

После обновления до Django 1.5 я начал получать такие ошибки:

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

Я установил ALLOWED_HOSTS = ['.derekkwok.net'] в своем файле settings.py.

Что здесь происходит? Кто-то притворяется Google и заходит на мой сайт? Или это мягкий случай, когда кто-то неправильно установил свой заголовок HTTP_HOST?

Дерек Квок
источник
Вы придумали, как это исправить? Столкнувшись с той же проблемой. Ежедневно регистрируется около сотни таких ошибок. Не знаю, стоит ли мне об этом беспокоиться.
Blinduck
3
Это сообщение в блоге предоставляет хороший способ прекратить рассылку
Дерек Квок,

Ответы:

64

Если ваш ALLOWED_HOSTS установлен правильно, возможно, кто-то исследует ваш сайт на предмет уязвимости, подделав заголовок.

Сейчас разработчики Django обсуждают, как изменить это с 500 внутренней ошибки сервера на ответ 400. Смотрите этот билет .

Брайан Нил
источник
1
Я думаю, что более вероятное объяснение - это поисковые роботы (роботы), которые просто сканируют общедоступные IP-адреса через порт 80 - и в этом случае вы захотите разрешить им это.
markmnl
16
@markmnl Законный поисковый робот не должен подделывать заголовки хоста.
Брайан Нил
1
Это просто подключение с использованием IP-адреса, а не доменного имени, и IP-адрес не входит в ALLOWED_HOSTS - или, по крайней мере, это то, что происходило со мной - я мог бы воспроизвести его, указав свой браузер на IP-адрес.
markmnl
Ага. И на любом наполовину загруженном сайте это происходит каждый день целый день. Они исправили это сейчас, но вот «всплывающее» приложение, которое сортирует его по всем версиям вместе с фильтром частоты ошибок. github.com/litchfield/django-safelogging
s29,
После размещения моего сайта в Интернете. Я обнаружил, что многие люди пытаются получить доступ к моему сайту, используя недопустимый хост. Не только с использованием IP-адреса. Я думаю, что это могут быть люди, пытающиеся найти веб-сайт, который не может защитить атаку csrf.
ramwin
131

Если вы используете Nginx для пересылки запросов к Django, работающему на Gunicorn / Apache / uWSGI, вы можете использовать следующее, чтобы заблокировать неверные запросы. Спасибо @PaulM за предложение и это сообщение в блоге за пример.

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
Было бы замечательно видеть в этом улучшение подсказки документации :)
Пол Макмиллан
1
@webjunkie, из вашей ссылки: «Бывают случаи, когда вы просто не можете избежать использования if, например, если вам нужно протестировать переменную, не имеющую эквивалентной директивы». В моем примере он используется правильно и хорошо работает в моей производственной среде. Итак, в заключение, сделайте это вот так! :)
Брент О'Коннор
2
Что ж, вы можете легко этого избежать: просто укажите только нужное вам server_name, а остальное пусть обработает обработчик сервера по умолчанию.
webjunkie
1
См. Этот ответ для аналогичной конфигурации Apache: stackoverflow.com/a/18792080
Denilson Sá Maia
1
Из ссылки, предоставленной webjunkie: «Директива, если возникают проблемы при использовании в контексте местоположения». В примере, приведенном Брентом, используется ifвнутренний serverблок, а не locationблок. Означает ли это, что ifв данном случае все в порядке?
Брайан Бак
31

При использовании Nginx вы можете настроить свои серверы так, чтобы запросы только к хостам, которые вы хотите получить в Django в первую очередь. Это больше не должно вызывать у вас ошибок 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
Мне нравится этот подход по сравнению с ifподходом, предложенным Брентом, но я не могу заставить его работать с портом 443. Я попытался имитировать ваше предложение (с измененным портом прослушивания), и мой фактический сайт SSL не загружается - он попадает в эту запись, которую я добавил. Есть идеи, как исправить?
Долан Антенуччи
1
У другого плаката на ServerFault.com были похожие проблемы, поэтому я последовал его рекомендации по подходу с выражением if только для трафика 443
Долан Антенуччи
1
Похоже, вам нужно указать путь к файлам сертификатов, если вы хотите также перехватывать запросы SSL (даже если вы просто хотите отбросить): 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
Что вернет Nginx, если HOST запроса недействителен? 50х или 40х?
laike9m
Что еще в этой конфигурации? У меня есть имя сервера, установленное как в перенаправлениях, так и в разделе приложений, я все еще получаю Invalid HTTP_HOST header(с Django 1.8.x)
Csaba Toth
16

Это исправлено в новых версиях Django, но если вы используете уязвимую версию (например, 1.5), вы можете добавить фильтр в обработчик логгера, чтобы избавиться от них, как описано в этом сообщении блога.

Спойлер:

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
Есть ли ссылка на исправление или версию, где она реализована? Thx
Marc
1
У меня было это на версии 2.0.5
Мехмет
Это не исправлено в новых версиях Django. Я использую Django 2.0.10
javidazac