# теория · шаг 1 из 4
Кто ответит на неизвестное имя
Коротко
Запрос с незнакомым Host не отбрасывается - его получает первый блок на
этом адресе и порту. То есть, скорее всего, твой настоящий сайт.
- Управляется флагом
default_server: он назначает блок-заглушку явно, один раз на каждый адрес и порт. server_name _;- не «любое имя», а просто заведомо невозможное имя.return 444закрывает соединение молча: ни заголовков, ни тела.- На 443 заглушка нужна тоже, и с nginx 1.19.4 ей не нужен сертификат:
ssl_reject_handshake on;обрывает рукопожатие, не показав чужому домену вообще ничего. - Заглушку можно обойти более точным адресом в чужом
listen- это ловушка урока.
Если catch-all и ssl_reject_handshake уже стоят - листай до «Теперь сам».
Сначала ответь сам
Твой сервер по адресу 203.0.113.7 (адрес для примера), на нём живёт shop.local. Приходит
запрос:
GET / HTTP/1.1
Host: totally-unrelated-domain.com
Кто-то направил свой домен на твой адрес - по ошибке, ради эксперимента или намеренно. Что ответит nginx?
Инстинктивный ответ - «ничего, имя же не совпало». Настоящий: ответит первый блок на этом порту, то есть твой магазин. Он отдаст свои страницы под чужим доменом, и в поиске может завестись копия сайта, которой ты не управляешь.
На что это похоже
Это дежурный, которому не сказали, что делать с посетителями без пропуска.
В инструкции написано, кого пускать: список фамилий. Про остальных не написано ничего - и дежурный поступает так, как поступают в отсутствие правила: отправляет человека к первой двери по коридору. Не к правильной, не к охраняемой - к первой.
Пока все приходят по списку, это незаметно. Заметно становится в тот день, когда за первой дверью оказывается бухгалтерия.
Механизм: полный алгоритм выбора блока
Собираем всё, что было в разделе, в одну картину.
Сама заглушка выглядит так:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
default_server назначает блок по умолчанию явно. Флаг ставится один раз
на адрес и порт; второму блоку nginx скажет
a duplicate default server for 0.0.0.0:80.
server_name _; - не «любое имя» и не служебный символ. Подчёркивание -
обычное имя, которое заведомо не совпадёт ни с одним настоящим доменом.
Годится любая невозможная строка, _ прижилось как соглашение.
return 444 - нестандартный код самого nginx: закрыть соединение, не
отвечая. Сканер не получает ни заголовков, ни тела, ни повода возвращаться.
Такую заглушку ещё называют catch-all - «ловит всё».
Правило
У каждой пары «адрес и порт» есть блок по умолчанию. Не назначил его флагом
default_server - им стал первый блок в собранном конфиге. Заглушка нужна на
каждый адрес и порт, где ты слушаешь.
Разбор: как выглядит работающая заглушка
Проверено на nginx 1.31.5, коды curl настоящие.
server { listen 80 default_server; server_name _; return 444; }
server { listen 80; server_name shop.local; return 200 "магазин\n"; }
for h in shop.local chuzhoy.local; do printf '%-14s -> ' $h; curl -s -H "Host: $h" localhost/ || echo "curl завершился с кодом $?"; done
shop.local -> магазин
chuzhoy.local -> curl завершился с кодом 52
|| выполняет вторую команду, только если первая завершилась неудачей, а $?
- её код. Пятьдесят второй код curl - «пустой ответ от сервера». Клиент соединился и не
получил ничего: ни строки статуса, ни заголовков. Для сканера это тупик, для
человека выглядит как «сервер не отвечает» - поэтому иногда вместо 444 отдают
честный 404. Вопрос того, кого ты ждёшь по этому адресу.
Отдельная история - 443-й порт. Соединение там начинается с
TLS-рукопожатияРазбирается в разделе 9, глава «Сертификат, цепочка и ключ»: Рукопожатие: что происходит до первого запроса - договора о шифровании, и
сертификат сервер предъявляет до того, как увидит Host. Просто написать
listen 443 ssl в заглушке не выйдет:
nginx: [emerg] no "ssl_certificate" is defined for the "listen ... ssl" directive
Раньше на это отвечали «положи туда сертификат основного сайта». С nginx 1.19.4 есть ответ лучше:
server {
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
}
Сертификат этому блоку не нужен вовсе, и чужой домен не увидит твой:
openssl s_client -connect localhost:443 -servername chuzhoy.example.com </dev/null
40076113577F0000:error:0A000458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name:../ssl/record/rec_layer_s3.c:1593:SSL alert number 112
no peer certificate available
openssl s_client - программа, которая открывает TLS-соединение как браузер:
-connect - куда, -servername - какое имя сайта назвать, `
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий