Недійсний заголовок HTTP_HOST Django's SuspiciousOperation


95

Після оновлення до 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
Цей допис у блозі пропонує приємний спосіб зупинити електронні листи: tiwoc.de/blog/2013/03/…
Дерек Квок,

Відповіді:


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

130

Якщо ви використовуєте 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
Було б чудово сприймати це як покращення натяку на підказки щодо документів :)
Paul McMillan

1
@webjunkie, З вашого посилання: "Бувають випадки, коли ви просто не можете уникнути використання if, наприклад, якщо вам потрібно протестувати змінну, яка не має еквівалентної директиви." Мій приклад використовує його правильно і добре працює у моєму виробничому середовищі. Отже, на закінчення, РОБІТЬ робити це так! :)
Брент О'Коннор

2
Ну, цього можна легко уникнути: просто вкажіть лише ім'я сервера, яке вам потрібно, а решту обробляє обробник сервера за замовчуванням.
webjunkie

1
Дивіться цю відповідь для подібної конфігурації Apache: stackoverflow.com/a/18792080
Denilson Sá Maia

1
З посилання, наданого webjunkie: "Директива, якщо виникають проблеми при використанні в контексті розташування". Приклад, наведений Brent, використовує ifвсередині serverблоку, а не в locationблоці. Чи означає це, що ifв цьому випадку це нормально?
Брайан Бак

31

Використовуючи Nginx, ви можете налаштувати свої сервери таким чином, щоб лише запити до хостів, до яких ви хочете, перейдіть до Django. Це більше не призведе до помилок підозрілої роботи.

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підходу, запропонованого Brent, але я не можу змусити його працювати з портом 443. Я намагався імітувати вашу пропозицію (із зміненим портом прослуховування), і мій фактичний сайт SSL не завантажується - він потрапляє в цей запис, який я додав. Будь-які ідеї щодо того, як це виправити?
Долан Антенуччі

1
Ще одна публікація на ServerFault.com мала подібні проблеми, тому я дотримувався його рекомендацій щодо підходу if-statement лише для 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 запиту недійсний? 50x чи 40x?
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
Марк

1
Я мав це у версії 2.0.5
Мехмет

Це не виправлено в нових версіях Django. Я використовую Django 2.0.10
javidazac
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.