1. 1
  2. 2
  3. 3
  4. 4

# теория · шаг 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?

Инстинктивный ответ - «ничего, имя же не совпало». Настоящий: ответит первый блок на этом порту, то есть твой магазин. Он отдаст свои страницы под чужим доменом, и в поиске может завестись копия сайта, которой ты не управляешь.

На что это похоже

Это дежурный, которому не сказали, что делать с посетителями без пропуска.

В инструкции написано, кого пускать: список фамилий. Про остальных не написано ничего - и дежурный поступает так, как поступают в отсутствие правила: отправляет человека к первой двери по коридору. Не к правильной, не к охраняемой - к первой.

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

Механизм: полный алгоритм выбора блока

Собираем всё, что было в разделе, в одну картину.

1. адрес и порт соединения 2. более точный адрес 3. четыре ступени имени нет 4. блок по умолчанию с флагом default_server, а без флага - первый Блок по умолчанию срабатывает ВСЕГДА, когда имя не совпало. Назначил ты его сам или он достался по порядку - вопрос только в этом. У каждого адреса и порта свой блок по умолчанию - и это ловушка.
Не совпало ни одно имя - запрос уходит в блок по умолчанию, четвёртый шаг алгоритма (не путать с четвёртой ступенью имён из прошлого урока). Он не исчезает: у каждой пары «адрес и порт» есть блок по умолчанию, назначенный флагом или доставшийся по порядку.

Сама заглушка выглядит так:

/etc/nginx/conf.d/00-default.confhttp
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 настоящие.

/etc/nginx/conf.d/00-default.confhttp
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
↳  вывод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 -t
nginx: [emerg] no "ssl_certificate" is defined for the "listen ... ssl" directive

Раньше на это отвечали «положи туда сертификат основного сайта». С nginx 1.19.4 есть ответ лучше:

/etc/nginx/conf.d/00-default.confhttp
server {
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
}

Сертификат этому блоку не нужен вовсе, и чужой домен не увидит твой:

$  команда
openssl s_client -connect localhost:443 -servername chuzhoy.example.com </dev/null
↳  вывод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 - какое имя сайта назвать, `

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.