Является ли ошибка Django 500 Invalid HTTP_HOST header проблемой безопасности?

У меня есть пользовательское веб-приложение Django, расположенное за прокси-сервером NGINX.

Я наблюдаю периодические ошибки, приходящие с моего сервера Django с сообщениями типа

Invalid HTTP_HOST header: 'my.domain.com:not-a-port-number'. The domain name provided is not valid according to RFC 1034/1035.

где часть после двоеточия представляла собой разнообразный мусор типа

  • my.domain.com:§port§/api/jsonws/invoke
  • my.domain.com:xrcr0x4a/?non-numeric-port-bypass
  • my.domain.com:80@+xxxxtestxxx/
  • my.domain.com:80@+${12311231}{{12311231}}/

Я могу воспроизвести аналогичную ошибку с помощью curl, где url запроса и заголовок хоста отличаются:

curl https://my.domain.com --header 'Host: my.domain.com:not-a-port-number'

Я подозреваю, что они, скорее всего, исходят от нашего программного обеспечения для сканирования сетевой безопасности, пытающегося найти уязвимости в наших пользовательских веб-приложениях, но я немного удивлен тем, что NGINX позволяет этим запросам проходить через мой сервер Django, особенно если, как показывает результат ошибки 500, это неправильно отформатированные заголовки.

Пытаясь подготовиться к худшему, есть ли что-то, что мне следует изменить или о чем следует побеспокоиться ради безопасности? Есть ли лучшая практика для этой ситуации, о которой я не знаю? Должен ли NGINX отфильтровывать подобные запросы?

Для моего собственного удобства было бы неплохо не видеть шум этих 500 ошибок, исходящих от Django, пока я нахожусь в поиске реальных ошибок на уровне приложения, но просто скрыть оповещения кажется плохим решением.

Дополнительная информация:

У меня ALLOWED_HOSTS установлен на 'my.domain.com' в моем файле Django settings.py.

Конфигурация NGINX:

server {
        listen 80 default_server;
        return 444;
}

server {
        listen 80;
        server_name my.domain.com;
        return 301 https://$server_name$request_uri;
}

server {
        listen 443 ssl default_server;
        ssl_certificate     /path/to/cert.cabundle.pem;
        ssl_certificate_key /path/to/cert.key;
        return 444;
}

# App https server.
# Serve static files and pass requests for application urls to gunicorn socket.
server {
        listen 443 ssl;
        server_name my.domain.com;

        ssl_certificate     /path/to/cert.cabundle.pem;
        ssl_certificate_key /path/to/cert.key;

        ... 

        location / {
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://unix:/path/to/gunicorn.sock;
        }
}

Я никогда не встречался с такой ситуацией на практике, однако могу предположить, что nginx выбирает ваш последний блок сервера для обработки запроса в соответствии с информацией из расширения SNI Client Hello, которая по какой-то причине (malformed request from scanning software?) отличается от значения заголовка Host в зашифрованном запросе.

Если вы хотите фильтровать эти запросы на уровне nginx, вы можете попробовать следующее:

server {
    listen 443 ssl;
    server_name my.domain.com;

    ssl_certificate     /path/to/cert.cabundle.pem;
    ssl_certificate_key /path/to/cert.key;

    if ($http_host != my.domain.com) {
        return 444;
    }

    ...
}

PS. По соображениям безопасности я никогда не использую свой настоящий сертификат в блоке сервера-заглушки, как этот:

server {
    listen 443 ssl default_server;
    ssl_certificate     /path/to/cert.cabundle.pem;
    ssl_certificate_key /path/to/cert.key;
    return 444;
}

Используйте самоподписанный, как показано здесь. Причина очевидна - дорогой хакер, если ты не знаешь, какой именно домен ты сейчас сканируешь, я не собираюсь говорить тебе, что это такое.

Вернуться на верх