Приложение формирует ссылки для сброса пароля из заголовка Host запроса. Как проверить, можно ли заставить его отправить пользователю ссылку на контролируемый домен?
Нужно отправить запрос на сброс пароля с изменённым значением Host и проверить фактическую ссылку в письме или другом канале доставки. Уязвимость подтверждена, если приложение использует переданное значение для построения абсолютного URL и формирует ссылку на домен тестировщика, особенно если токен восстановления попадает в этот URL.
Проверка должна учитывать также заголовки, которыми прокси передаёт исходный хост, например X-Forwarded-Host. Безопасное приложение использует заранее заданный доверенный внешний адрес либо принимает только разрешённые значения хоста.
HTTP-запрос содержит имя узла, к которому обращается клиент, поэтому веб-серверы и приложения часто используют значение Host для выбора виртуального хоста. Позднее за прокси и балансировщиками появились дополнительные заголовки, передающие исходные свойства запроса.
Проблема возникла, когда приложения начали использовать эти значения не только для маршрутизации, но и для генерации ссылок, писем и перенаправлений. Если входной заголовок считается достоверным без проверки, внешний пользователь может влиять на адрес, который приложение показывает или отправляет другим людям.
Механизм сброса пароля обычно отправляет пользователю абсолютную ссылку с одноразовым токеном. Если домен этой ссылки строится из неподтверждённого заголовка запроса, атакующий может инициировать сброс для чужой учётной записи и добиться отправки ссылки на свой домен.
Даже если токен не раскрывается автоматически, такая ссылка повышает риск фишинга. Если пользователь перейдёт по ней, токен может попасть на контролируемый сервер через URL, журналы, заголовок Referer или средства аналитики — в зависимости от реализации страницы восстановления.
Тестировщик должен:
Важно проверять не только отображаемый текст ссылки, но и фактический адрес назначения. Если приложение отвергает неизвестный Host, это должно происходить до генерации ссылки. Простого сравнения строк без учёта регистра, порта, формата имени узла и особенностей прокси недостаточно: политика доверенных хостов должна быть явной и согласованной между инфраструктурой и приложением.
Наиболее надёжный вариант — хранить канонический внешний адрес в конфигурации и не получать его из пользовательского запроса. Альтернатива — строгий список разрешённых хостов, но он требует аккуратного управления окружениями и доменами. Доверять X-Forwarded-Host можно только от конкретного прокси, который гарантированно удаляет входное значение и сам устанавливает корректный заголовок.
Открытое перенаправление и подмена хоста связаны с перенаправлением пользователя на чужой адрес, но это разные дефекты. Здесь ключевой признак — приложение само формирует ссылку восстановления на основе неподтверждённого имени узла.
В тестовой среде запрос на восстановление проходил через балансировщик. Балансировщик корректно изменял Host, но приложение дополнительно доверяло X-Forwarded-Host, сохраняя пришедшее от клиента значение. В результате письмо содержало ссылку на внешний домен тестировщика.
Рассматривались два варианта. Первый — фильтровать заголовок на балансировщике: это быстро, но оставляет риск при другом маршруте трафика или ошибочной настройке прокси. Второй — задать канонический адрес восстановления в конфигурации приложения: это требует отдельной настройки для каждого окружения, зато не зависит от пользовательских заголовков.
Был выбран второй вариант, а на прокси дополнительно добавили проверку допустимых хостов. После исправления неизвестные значения отклонялись, ссылки во всех уведомлениях использовали только зарегистрированный адрес, а отдельные тесты проверяли прямой доступ к приложению в обход балансировщика.
1. Достаточно ли проверить только заголовок Host?
Нет. В реальной схеме приложение может использовать X-Forwarded-Host, Forwarded или внутренний параметр, который устанавливает прокси. Нужно определить фактический источник внешнего URL и проверить каждый путь доставки запроса. При этом нельзя механически считать любой такой заголовок атакующим: его доверие зависит от того, кто его устанавливает и удаляет ли прокси исходное значение.
2. Всегда ли подмена домена означает кражу токена сброса?
Нет. Кража зависит от реализации страницы восстановления. Токен может быть одноразовым, привязанным к серверной сессии, передаваться во фрагменте URL или сразу обмениваться на другой маркер. Однако подмена домена всё равно подтверждает нарушение доверия и создаёт фишинговый риск; возможность перехвата токена нужно проверять отдельно по фактическому поведению клиента и сервера.
3. Почему нельзя исправить проблему только проверкой формата Host?
Проверка формата подтверждает, что значение похоже на доменное имя, но не что оно принадлежит приложению. Корректно сформированный домен атакующего также пройдёт такую проверку. Нужна проверка принадлежности доверенному списку либо отказ от использования входного значения в пользу заранее настроенного канонического URL.