С чего всё начинается: почему сервер вообще может не понять, где кончается запрос
Чтобы разобраться в HTTP Request Smuggling, надо сначала честно ответить на вопрос, который в обычной жизни может прозвучать абсурдно: а как сервер понимает, где заканчивается один HTTP-запрос и начинается следующий? Кажется, что ответ очевиден — запрос это запрос, отправил и отправил. Но эта очевидность держится ровно до тех пор, пока запрос один и сервер один. Как только их становится много, а серверов в цепочке хотя бы два, выясняется, что «границу запроса» надо где-то явно записать, и способов записать её оказывается не один, и вот в этом зазоре и живёт вся уязвимость.
Начнём с того, как устроена современная доставка запроса от браузера до кода приложения. Почти никогда браузер не соединяется напрямую с тем сервером, который выполняет логику. Между ними стоит цепочка: сначала фронтенд — балансировщик нагрузки, реверс-прокси, CDN-узел, — который принимает соединение от клиента, а затем передаёт запрос дальше, на бэкенд, где живёт собственно приложение. Это не архитектурное излишество, а норма: фронтенд терминирует TLS, распределяет нагрузку, кэширует, фильтрует, и без него облачный сервис почти не строится.
Ключевая деталь в том, как фронтенд общается с бэкендом. Открывать новое TCP-соединение под каждый запрос дорого, поэтому фронтенд держит с бэкендом постоянные соединения и переиспользует их: по одному и тому же соединению он отправляет запрос за запросом, встык, друг за другом. И вот здесь…