Является ли ошибка 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;
}
Используйте самоподписанный, как показано здесь. Причина очевидна - дорогой хакер, если ты не знаешь, какой именно домен ты сейчас сканируешь, я не собираюсь говорить тебе, что это такое.