Для чего следует всегда настраивать сайт по умолчанию?
- Интернет безостановочно сканируется многочисленными автоматическими инструментами.
Например, публично доступные утилиты ZMap и masscan
способны при относительно быстром подключении проверить ВЕСЬ диапазон существующих IPv4-адресов за 5-6 минут.
- Как правило, отправной точкой для автоматических сканеров являются только IP-адреса, других исходных данных они не имеют.
- Если сайт по умолчанию в настройках Nginx не указан, на запросы к сайтам без имени
(то есть с пустым полем “Host:” в заголовке HTTP-запроса) или с неизвестными именами
(в том числе с IP-адресом в “Host:”) Nginx будет отвечать страницами первого настроенного сайта.
- Такой подход (а) создаёт повышенную и ненужную нагрузку на сервер и (б) сообщает сканеру информацию,
которая может быть использована для последующих атак — особенно, если сайт динамический.
- Поэтому всем посетителям веб-сервера, не знающим точного имени одного из размещённых на нём сайтов,
рекомендуется возвращать максимально быстрый и максимально неинформативный ответ.
Как следует настраивать сайт по умолчанию?
- В большинстве дистрибутивов Nginx уже содержит /etc/nginx/sites-available/default,
который, если удалить из него всё лишнее, выглядит примерно так:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
Для наших целей его можно улучшить двумя способами: сократить (убрать root, index, server_name и location)
и ускорить (заменить чтение с диска встроенным ответом):
server {
listen 80 default_server;
listen [::]:80 default_server;
server_tokens off;
default_type "text/html"; # ..instead of application/octet-stream
return 200 'WakeUp, Neo.';
}
Почему этого недостаточно?
- Для большинства сайтов сейчас принято использовать не HTTP, а HTTPS.
- В отличие от HTTP, настроек по умолчанию для HTTPS в Nginx нет.
- Поэтому на HTTPS-запросы с пустым, неизвестным или числовым “Host:”, как объяснялось выше, Nginx будет отвечать первым сайтом,
что нежелательно (и так же объяснялось выше).
Как должен выглядеть HTTPS-сайт по умолчанию?
- Почти так же, как и сайт для HTTP, с одним отличием (порт-протокол) и одним дополнением (ключ-сертификат):
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate default.crt;
ssl_certificate_key default.key;
server_tokens off;
default_type "text/html"; # ..instead of application/octet-stream
return 200 'WakeUp, Neo.';
}
Как следует генерировать default.crt и default.key?
- Сгенерировав один раз, в дальшнейшем их незачем продлевать — в просроченном виде они будут выполнять свою работу так же хорошо, как в
свежем.
- Их незачем (и не получится) заверять их у SSL-регистратора — самоподписанные нас полностью устраивают
(и даже предпочтительнее заверенных, т.к. имя нашего заверителя в сертификате является информацией, которую владелец сканера может
использовать при расширенной атаке).
- Самое главное — в них не должно попасть ни имя сервера из hostname, ни имена сайтов, чтобы сканеры не могли узнать эту информацию,
просто подключившись по IP-адресу на 443 порт.
- Утечка “IP-адрес => HTTPS-запрос => hostname из сертификата” отчасти напоминает классическую “IP-адрес => DNS-запрос =>
hostname из PTR-записи” (из-за которой многие руководства по сетевой безопасности рекомендуют не создавать PTR-записи для защищаемых
серверов).
- Примеры таких утечек доступны на портале Shodan.io, который сканирует IP-адреса, собирает hostnames и предоставляет публичный поиск обратных
зависимостей,
т.е. для заданного hostname или домена показывает IP-адреса серверов, сообщивших его сканеру в ответ на пустой запрос.
- Например, популярный портал Censor.net «спрятан» за Cloudflare, но https://www.shodan.io/search?query=censor.net
сразу показывает нам фактическое расположение серверов (настоящее имя сканеру выдали как сертификат, так и сам сайт):
- Таким образом, нам не подходят сертификаты, которые генерирует дистрибутивный пакет ssl-cert и прочие Snakeoil-обертки над OpenSSL
(см. https://wiki.debian.org/Self-Signed_Certificate), потому
что они записывают в сертификат реальный hostname.
- Этого недостатка оказался лишён вариант, позаимствованный нами у https://letsencrypt.org/docs/certificates-for-localhost/.
cd /etc/nginx &&
openssl req -x509 -out default.crt -keyout default.key \
-newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <(printf "
[dn]
CN=localhost
[req]
distinguished_name = dn
[EXT]
subjectAltName=DNS:localhost
keyUsage=digitalSignature
extendedKeyUsage=serverAuth")