Сводка

28.10.2026

Подмена Referer, Host и X-Forwarded-For: почему нельзя верить заголовкам

6 мин чтенияHTTPзаголовкиnginx

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 — лишь дополнительный барьер, настроенный на уровне сервера, где адрес нельзя подделать.

Всё, что пришло в запросе, написал клиент. Заголовки — это сведения, а не доказательства.

  1. Реферальные начисления — только по ссылкам с подписью HMAC.
  2. CSRF — токенами и SameSite-cookie, без опоры на Referer.
  3. Ссылки в письмах — из конфигурации, неизвестный Host — отклонять в nginx.
  4. X-Forwarded-For — только от доверенных прокси через set_real_ip_from.
  5. Доступ — пароль и 2FA; IP-фильтр — на уровне сервера и только как дополнительный слой.