Выберите продукт

HTTP Request Smuggling в цепочке Nginx, HAProxy и Apache: практика защиты

HTTP Request Smuggling — класс атак на разрыв трактов парсинга между прокси и бекендом. Разбираем практику для Nginx, HAProxy и Apache: где возникают CL.TE и H2/H2C‑конверсии, как настроить фильтрацию заголовков и буферизацию, что логировать.
HTTP Request Smuggling в цепочке Nginx, HAProxy и Apache: практика защиты

HTTP Request Smuggling (HRS, «десинхронизация запросов») — это класс атак, возникающий из‑за различий в том, как фронт‑прокси и бекенд парсят границы HTTP‑сообщений. Чаще всего уязвимости проявляются на стыке HTTP/1.1 и HTTP/2 (h2, h2c), в обработке заголовков Transfer-Encoding (TE) и Content-Length, в передаче/запрете hop‑by‑hop заголовков через Connection, а также при допущениях о безопасной конкатенации и повторном использовании соединений. Ниже — практическое руководство для Nginx, HAProxy и Apache: как понять риск и как закрыть его конфигурацией.

Коротко о причинах http request smuggling

Корень проблемы — нестыковка трактов. Фронт (CDN/балансировщик/реверс‑прокси) принимает запрос и пересобирает его для бекенда, а бекенд может интерпретировать границы тела иначе. Отсюда — «разъезд» потока: префикс одного запроса трактуется как следующий, что позволяет атакующему влиять на ответы к чужим запросам, обходить ACL, кэш и аутентификацию.

  • CL.TE: фронт выбирает Content-Length, бекенд — Transfer-Encoding: chunked.
  • TE.CL: обратная комбинация выбора.
  • H2→H1 конверсия: HTTP/2 запрещает TE, кроме TE: trailers, но при преобразовании в H1 некоторые прокси ошибочно переносят или подставляют TE, провоцируя десинхронизацию.
  • h2c (HTTP/2 без TLS) и Upgrade: смешение протоколов и промежуточных преобразований.
  • «Грязные» заголовки: повторяющиеся Content-Length, нестандартные пробелы, табы, нижние подчёркивания в именах, дубли Connection, Proxy-Connection, Trailer.

Ключевая цель защиты — чтобы на границе прокси тело запроса было однозначно прочитано, нормализовано и отправлено бекенду в предсказуемом виде (желательно с Content-Length), а все hop‑by‑hop заголовки были отброшены.

Модель угроз и где искать риск в стеке

Типичный стек: публичный фронт (Nginx или HAProxy) завершает TLS и HTTP/2, конвертирует в HTTP/1.1 и проксирует к внутренним сервисам (иногда к Apache/PHP‑FPM, иногда к приложению напрямую). Риск растёт при:

  • включённом keep‑alive и повторном использовании соединений к бекенду;
  • стриминговых режимах без буферизации (для больших аплоадов/проксирования), когда прокси не пересобирает запрос;
  • использовании h2c/Upgrade, смешении протоколов и нестандартных клиентов;
  • наличии внешнего CDN перед вашим фронтом с собственными нормализациями заголовков.

Для TLS‑терминации на периметре используйте актуальные сертификаты и отключайте h2c; при необходимости обновить сертификат посмотрите наши SSL-сертификаты.

Схема конверсии H2 в H1.1 с удалением hop‑by‑hop заголовков

Nginx: безопасные настройки против десинхронизации

Почему Nginx чаще в выигрыше

По умолчанию Nginx читает запрос клиента полностью (с proxy_request_buffering on) и формирует новый запрос к апстриму с явным Content-Length. Так разрушаются типичные схемы CL.TE/TE.CL. Риски появляются при отключении буферизации или при агрессивных апстрим‑оптимизациях.

Рекомендации по http‑блоку

http {
    # Запрет «грязных» заголовков и подчёркиваний
    underscores_in_headers off;
    ignore_invalid_headers on;

    # Контроль размеров заголовков и H2 фреймов
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;
    http2_max_field_size 8k;
    http2_max_header_size 32k;

    # Ограничения по телу и таймаутам
    client_max_body_size 20m;
    client_body_timeout 20s;

    # По умолчанию безопаснее буферизовать запросы
    proxy_request_buffering on;
}

Реверс‑прокси сервер с HTTP/2 на входе

server {
    listen 443 ssl http2;
    server_name example.test;

    # ... ssl_* настройки, не опускаем таймауты ниже разумных

    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;

        # Убираем hop-by-hop заголовки, чтобы они не «протекали» к апстриму
        proxy_set_header Connection "";
        proxy_set_header Proxy-Connection "";
        proxy_set_header TE "";
        proxy_set_header Transfer-Encoding "";
        proxy_set_header Trailer "";
        proxy_set_header Upgrade "";

        # Явно пробрасываем нужные end-to-end заголовки
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }
}

upstream app_backend {
    server 127.0.0.1:8080;
    keepalive 64;
}

Комментарии:

  • proxy_request_buffering on — основной барьер от HRS. При необходимости стриминга (загрузки больших файлов) отключайте буферизацию точечно и компенсируйте: закрывайте соединение к апстриму (proxy_set_header Connection close) или проксируйте на эндпоинт, изолированный от общего пула.
  • Удаление Connection, TE, Transfer-Encoding, Proxy-Connection, Upgrade, Trailer убирает почву для смещения границ тела на стороне бекенда.
  • Nginx не поддерживает универсальный HTTP/2 апстрим (кроме gRPC). Поэтому типичная связка — h2 на фронте, H1.1 на беке; тем важнее нормализация.

Диагностика и логи

log_format smuggle '$remote_addr "$request" $status $body_bytes_sent '
                   '$request_length $request_time '
                   'cl="$http_content_length" te="$http_transfer_encoding" conn="$http_connection"';
access_log /var/log/nginx/access_smuggle.log smuggle;

Поля $request_length, $http_transfer_encoding и $http_connection помогают быстро увидеть подозрительные запросы (TE, попытки протолкнуть hop‑by‑hop).

HAProxy: строгий парсинг и HTX

Ключевые опции

  • HTX‑режим нормализует HTTP представление и защищает от ряда неочевидных кейсов. В новых ветках он включён по умолчанию, но лучше задать явно.
  • option http-buffer-request — заставляет фронт прочитать запрос целиком до отправки к беку, снижая риск десинхронизации.
  • Удаление hop‑by‑hop заголовков и явный запрет на дубликаты Content-Length.
  • Отключение приёма устаревших символов H1: no option h1-accept-obs-chars.
global
    log 127.0.0.1 local0

defaults
    mode http
    option httplog
    option http-use-htx
    option http-buffer-request
    no option h1-accept-obs-chars
    timeout client 30s
    timeout connect 5s
    timeout server 30s

frontend fe_https
    bind :443 ssl alpn h2,http/1.1 crt /etc/haproxy/certs/bundle.pem

    # Уничтожаем hop-by-hop заголовки на входе
    http-request del-header Proxy-Connection
    http-request del-header Connection
    http-request del-header TE
    http-request del-header Transfer-Encoding
    http-request del-header Trailer
    http-request del-header Upgrade

    # Рубим подозрительные комбинации длины
    http-request deny if { req.hdr_cnt(content-length) gt 1 }
    http-request deny if { req.hdr(content-length) -m found } { req.hdr(transfer-encoding) -m found }

    default_backend be_app

backend be_app
    balance roundrobin
    # Для особо критичных путей можно закрывать соединение к беку:
    # option httpclose
    server s1 127.0.0.1:8080 check

Примечания:

  • Политика «удаляем TE/Transfer-Encoding» безопасна для большинства приложений. Если у вас действительно нужны трейлеры, добавьте валидирующее правило, разрешающее только TE: trailers, и всё равно удаляйте заголовок на границе перед бэком.
  • При терминаторе h2 на фронте и H1.1 к бэку HTX и http-buffer-request заметно снижают поверхность атаки.

Логи и метрики

log-format %ci:%cp [%t] %ft %b %s %TR/%Tw/%Tc/%Tr/%Ta bytes=%B cl:%[req.hdr(content-length)] te:%[req.hdr(transfer-encoding)]

Следите за частотой отказов по ACL и аномальной долей запросов с TE.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Apache: моды, строгий протокол и прокси

В Apache важны три направления: строгий парсинг HTTP, корректные лимиты и аккуратный mod_proxy к бэку. В сочетаниях с mod_http2 и mod_proxy_http ранее встречались уязвимости, поэтому держите актуальные минорные версии.

Строгий HTTP и размеры

# httpd.conf или внутри VirtualHost
HttpProtocolOptions Strict
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
LimitRequestFieldSize 8190
LimitRequestFields 100

Отключаем h2c и настраиваем протоколы

# Во избежание лишних Upgrade-переходов
H2Upgrade off
# Разрешаем h2 по TLS и H1.1
Protocols h2 http/1.1

Чистим hop-by-hop заголовки на входе

# mod_headers
RequestHeader unset Proxy-Connection
RequestHeader unset Connection
RequestHeader unset TE
RequestHeader unset Transfer-Encoding
RequestHeader unset Trailer
RequestHeader unset Upgrade

Безопасный reverse proxy в Apache

# Пример VirtualHost (угловые скобки экранированы)
<VirtualHost *:443>
    ServerName example.test

    # ... SSL настройки

    ProxyPreserveHost On
    ProxyPass "/" "http://127.0.0.1:8080/" disablereuse=on
    ProxyPassReverse "/" "http://127.0.0.1:8080/"

    # Для особо рисковых локаций можно отключать keep-alive к бэку:
    # ProxyPass "/upload/" "http://127.0.0.1:8080/upload/" disablereuse=on

    # Логи
    LogFormat "%h \"%r\" %>s %I %O cl:%{Content-Length}i te:%{Transfer-Encoding}i" vhostsmuggle
    CustomLog logs/smuggle_access.log vhostsmuggle
</VirtualHost>

Флаг disablereuse=on закрывает соединение к бэку после ответа — это снижает риск десинхронизации ценой небольшой потери производительности. Применяйте точечно на опасных путях (загрузки, нестандартные клиенты).

Политика заголовков: что точно не должно попадать к бэку

  • Connection и всё, что перечислено внутри него (включая Proxy-Connection, TE, Trailer, Upgrade, Keep-Alive).
  • Transfer-Encoding приёмлем только на стороне клиента к фронту; к бэку — лучше всегда Content-Length или заранее прочитанное тело.
  • Повторяющиеся или конфликтующие Content-Length должны приводить к 400.
  • Подчёркивания в именах заголовков, табы и нестандартные пробелы — отклонять.

Про связанные политики см. разбор заголовков защиты: Базовые HTTP Security Headers для Nginx и Apache. Для статики и кэша полезно также настроить Cache-Control и ETag.

H2, h2c и конверсия в H1.1: где подвох

HTTP/2 не допускает TE, кроме TE: trailers, и использует бинарные фреймы вместо чанков. При конверсии в H1.1 фронт должен:

  • отбрасывать любые TE от клиента;
  • самостоятельно сформировать Content-Length к бэку (после полного чтения тела) или закрыть соединение к бэку после передачи, если идёт поток;
  • не использовать h2c без крайней необходимости; Upgrade на публичных порталах — источник сложности.

Анализ логов Nginx и HAProxy с полями TE и Content-Length

Когда буферизация «нельзя, но очень хочется»

Потоковые аплоады, веб‑хуки и большие тела иногда требуют стримить запрос на бэк без полной буферизации. Как уменьшить риск:

  • Nginx: только для конкретных локаций ставьте proxy_request_buffering off и одновременно proxy_set_header Connection close к бэку; используйте отдельный апстрим‑пул.
  • HAProxy: включите option http-buffer-request хотя бы на контрольных путях; при острой необходимости — option httpclose на бэке для этих путей.
  • Apache: для потоковых путей используйте disablereuse=on в ProxyPass, а также строгие лимиты и таймауты чтения.

Чек‑лист быстрого аудита

  1. На фронте удаляются Connection, TE, Transfer-Encoding, Proxy-Connection, Trailer, Upgrade.
  2. Дубликаты Content-Length и конфликт с Transfer-Encoding приводят к 400.
  3. Буферизация запросов включена по умолчанию; стриминг — только точечно и с закрытием соединения к бэку.
  4. HTTP/2 включён только по TLS (h2), H2Upgrade off, h2c не используется на периметре.
  5. Ограничены размеры заголовков и тела; включены таймауты чтения.
  6. Логи фиксируют cl, te, размеры и время.

Безопасное тестирование (в стейджинге)

Тестируйте только в изолированной среде и с согласия ответственных. Цель — убедиться, что фронт не пропускает «грязные» заголовки и всегда нормализует запрос к бэку. Если стенда нет — поднимите отдельную VM на VDS и повторите проверки там.

  • Проверьте, что запросы с Transfer-Encoding: chunked не доходят в неизменном виде до бэка (по логам приложения).
  • Отправьте два Content-Length и убедитесь в 400 от фронта.
  • Снимите tcpdump/pcap между фронтом и бэком и проверьте, что тело к бэку идёт с явным Content-Length, а не с чанками.
  • С HTTP/2 на входе убедитесь, что при конверсии в H1.1 к бэку нет TE и иных hop‑by‑hop.

Наблюдаемость и алерты

Добавьте в алерты долю запросов с TE, частоту 400 по «bad request», всплески ошибок бэка после релизов прокси. Логи на фронте и бэке синхронизируйте по времени и коррелируйте по request-id, чтобы быстро ловить десинхронизацию.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Частые анти‑паттерны

  • «Отключили буферизацию глобально, чтобы ускорить загрузки» — делайте только точечно и с закрытием соединений к бэку.
  • «Передаём клиентские заголовки как есть» — hop‑by‑hop никогда не должен уходить к бэку.
  • «Включили h2c ради эксперимента» — на периметре это увеличивает сложность и риск.
  • «Слишком большие лимиты заголовков» — облегчает smuggling через необычные конструкции; держите лимиты в разумных пределах.

Итоги

http request smuggling — это не один баг, а класс ошибок на границе парсинга и нормализации HTTP. Защита сводится к простым принципам: буферизовать и пересобирать запрос, удалять hop‑by‑hop заголовки, избегать сомнительных режимов (h2c, стриминг без закрытия соединений), держать строгие лимиты и актуальные версии. В Nginx, HAProxy и Apache всё это достигается несколькими опциями — внедрите их один раз и закрепите проверками в CI.

Поделиться статьей

Вам будет интересно

systemd-networkd на VDS: пропал интернет после reboot — DHCP, cloud-init и метрики маршрутов OpenAI Статья написана AI (GPT 5)

systemd-networkd на VDS: пропал интернет после reboot — DHCP, cloud-init и метрики маршрутов

Если после reboot на VDS «отвалилась» сеть, чаще всего виноваты cloud-init/netplan, конкурирующие DHCP-клиенты или неверная метрик ...
CPU throttling на VDS в Linux: TDP/thermal лимиты, частоты и квоты cgroups v2 (cpu.max) OpenAI Статья написана AI (GPT 5)

CPU throttling на VDS в Linux: TDP/thermal лимиты, частоты и квоты cgroups v2 (cpu.max)

Если на VDS растёт load average и задержки, а CPU «не на 100%», причина часто в throttling: тепловые/мощностные лимиты, steal time ...
Let’s Encrypt через DNS-01: wildcard, автоматизация продления и безопасные deploy-хуки OpenAI Статья написана AI (GPT 5)

Let’s Encrypt через DNS-01: wildcard, автоматизация продления и безопасные deploy-хуки

DNS-01 удобен для wildcard и закрытых сетей: владение доменом подтверждается TXT-записью в DNS. Разбираем выбор certbot/lego/acme. ...