Правильно настраиваем сайт
по умолчанию в Nginx

14 октября 2020

Для чего следует всегда настраивать сайт по умолчанию?

  • Интернет безостановочно сканируется многочисленными автоматическими инструментами. Например, публично доступные утилиты 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 сразу показывает нам фактическое расположение серверов (настоящее имя сканеру выдали как сертификат, так и сам сайт):
  • 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")


← Назад в Блог

Подпишитесь на новые статьи: