Сводка
28.10.2026Подмена Referer, Host и X-Forwarded-For: почему нельзя верить заголовкам
HTTP-заголовки присылает клиент, а значит, их можно написать любыми. Разбираем, где доверие к Referer, Host и X-Forwarded-For превращается в дыру — от реферальных выплат до сброса пароля.
Каждый запрос к сайту сопровождается заголовками — служебными строками, которые сообщают серверу, откуда пришёл посетитель, какой у него браузер, по какому адресу он обращается. Браузер заполняет их автоматически, и из-за этого легко забыть главное: заголовки присылает клиент. Любой, кто отправляет запрос не из браузера, а из скрипта, может написать в них что угодно.
Пока заголовки используются для статистики, это не проблема. Проблема начинается, когда на их основе приложение принимает решения: начислить бонус, пропустить в админку, сформировать ссылку.
Referer: «я пришёл от партнёра»
Заголовок Referer показывает страницу, с которой посетитель перешёл на сайт. Многие обменники используют его в партнёрских программах: пришёл с сайта партнёра — заявка записывается на партнёра, ему начисляется процент.
Если начисление зависит только от Referer, партнёрскую выплату можно «заработать», вообще не приводя клиентов: достаточно отправлять запросы с нужным значением заголовка. Деньги уходят за трафик, которого не было.
Вторая типичная ошибка — защита от CSRF, основанная только на Referer. CSRF — это атака, при которой чужой сайт заставляет браузер вошедшего пользователя выполнить действие в вашем приложении. Проверка «запрос пришёл с нашего домена» по одному Referer ненадёжна: браузеры и расширения иногда вырезают этот заголовок, и разработчики, чтобы не ломать сайт, начинают пропускать запросы без него. Защита превращается в формальность.
Host: ссылка для сброса пароля ведёт не туда
Заголовок Host сообщает, к какому домену обращается клиент. Приложения часто используют его, чтобы собирать абсолютные ссылки — например, в письме «Сбросить пароль».
Если сервер принимает любой Host и подставляет его в ссылку, злоумышленник может инициировать сброс пароля для чужой учётной записи, указав в заголовке свой домен. Письмо уйдёт настоящему владельцу аккаунта — с настоящим оформлением, но со ссылкой на чужой сервер. Один клик — и секретный токен сброса оказывается у атакующего. Это называют отравлением заголовка Host.
X-Forwarded-For: чужой IP-адрес
Когда сайт стоит за балансировщиком или CDN, приложение видит не адрес посетителя, а адрес прокси. Настоящий IP передаётся в заголовке X-Forwarded-For. Но если приложение читает этот заголовок, не проверяя, кто его прислал, посетитель может вписать туда любой адрес.
Последствия зависят от того, где используется IP:
- белые списки IP для админки — злоумышленник «представляется» адресом офиса;
- ограничение частоты запросов — каждая попытка подбора пароля приходит «с нового адреса», и лимит не срабатывает;
- геоблокировка — запрет для определённых стран обходится одной строкой;
- журналы — в логах остаются выдуманные адреса, и расследование инцидента идёт по ложному следу.
Origin и User-Agent
Заголовок Origin надёжнее Referer для проверки источника запросов из браузера, но и его легко подделать вне браузера. Он полезен как дополнительный сигнал, а не как пропуск.
User-Agent — строка с названием браузера — подделывается тривиально. Решения вида «запросы от нашего мобильного приложения узнаём по User-Agent» не защищают ни от чего.
Как понять, есть ли проблема у вас
Начните с инвентаризации: найдите в коде все места, где читаются Referer, Host, X-Forwarded-For, Origin и User-Agent, и для каждого ответьте на вопрос — что произойдёт, если значение подделано? Если ответ «ничего, это только для статистики» — всё в порядке. Если «начислится бонус», «пропустит в админку» или «изменится ссылка в письме» — это место нужно переделать. Отдельно проверьте конфигурацию nginx и список доверенных прокси в приложении: они должны совпадать с реальной схемой вашей инфраструктуры.
Как защищаться
Подписывать реферальные ссылки. Партнёр получает ссылку с параметром, подписанным HMAC — криптографической подписью на секретном ключе сервера. Начисление происходит только по ссылке с верной подписью, а не по заголовку.
CSRF — только токенами. Каждая форма и каждый запрос, меняющий данные, должны нести токен, который сервер выдал этому пользователю. В Laravel это работает из коробки — важно не отключать проверку «для удобства». Дополнительно помогают cookie с атрибутом SameSite.
Фиксированный канонический домен. Абсолютные ссылки в письмах собираются из адреса в конфигурации приложения, а не из Host. На уровне nginx запросы с неизвестным Host лучше отклонять отдельным серверным блоком по умолчанию.
Доверять X-Forwarded-For только от своих прокси. В nginx для этого есть модуль real_ip: директива set_real_ip_from перечисляет адреса ваших балансировщиков или CDN, и только от них заголовок принимается. В Laravel аналогичную роль играет список доверенных прокси. Всё остальное — адрес соединения, а не то, что написал клиент.
Не принимать решений о доступе по заголовкам. Вход в админку — по паролю и второму фактору, а белый список IP — лишь дополнительный барьер, настроенный на уровне сервера, где адрес нельзя подделать.
Всё, что пришло в запросе, написал клиент. Заголовки — это сведения, а не доказательства.
- Реферальные начисления — только по ссылкам с подписью HMAC.
- CSRF — токенами и SameSite-cookie, без опоры на Referer.
- Ссылки в письмах — из конфигурации, неизвестный Host — отклонять в nginx.
- X-Forwarded-For — только от доверенных прокси через
set_real_ip_from.- Доступ — пароль и 2FA; IP-фильтр — на уровне сервера и только как дополнительный слой.