Разбор последствий: токен сброса пароля передают в query-параметре URL. Какой архитектурный риск это создаёт?
Основной риск — утечка токена сброса через побочные каналы, не предназначенные для передачи секретов: журналы веб-сервера и прокси, историю браузера, системы мониторинга, аналитику и заголовок Referer. Поскольку токен обычно является временным bearer-секретом, тот, кто его получит до истечения срока действия, может сбросить пароль.
HTTPS защищает передачу URL по сети, но не предотвращает появление URL в этих вторичных хранилищах. Поэтому токен в URL допустим только при сочетании короткого срока жизни, одноразового использования и минимизации его дальнейшего распространения.
URL изначально проектировался как адрес ресурса, который удобно сохранять, передавать, журналировать и показывать пользователю. Веб-инфраструктура традиционно записывает URL в access-журналы, историю браузера и инструменты диагностики.
Ссылки в электронных письмах стали удобным способом запуска операций сброса пароля, поэтому секрет часто помещали прямо в URL. Это обеспечило совместимость с почтовыми клиентами, но создало конфликт между удобством кликабельной ссылки и требованиями к конфиденциальности временных учётных данных.
Токен в query-параметре может попасть в журналы балансировщика, CDN, reverse proxy, веб-сервера, систем мониторинга и аналитики. Он также может сохраниться в истории браузера или быть передан внешнему ресурсу через Referer, если страница после перехода загружает сторонний контент.
Неверно спроектированная система может принять такой токен многократно, не ограничить его срок действия или не привязать его к конкретной операции. Тогда утечка одного URL превращается в возможность захватить учётную запись, даже если пароль пользователя и канал доставки письма не были скомпрометированы.
Токен нужно рассматривать как одноразовое временное право на выполнение конкретной операции, а не как обычный идентификатор. Он должен быть непредсказуемым, иметь короткий срок жизни, храниться на сервере в форме, пригодной для безопасной проверки, и погашаться атомарно при успешном использовании.
После открытия ссылки безопаснее немедленно обменять токен на серверную состояние сброса и перенаправить пользователя на чистый URL без секрета. Форма смены пароля должна отправлять данные отдельным защищённым запросом, а страница не должна загружать сторонние ресурсы до удаления токена из адресной строки.
Для снижения утечек применяют ограничительную Referrer-Policy, исключают секретные URL из прикладных журналов и маскируют query-параметры в системах наблюдаемости. Однако это дополнительные меры: они не заменяют одноразовость и ограниченный срок действия токена.
Перемещение токена во фрагмент URL после символа решётки предотвращает его отправку серверу в обычном HTTP-запросе, но требует клиентского кода для обмена токена. Такой вариант уменьшает риск серверного журналирования, однако повышает зависимость от безопасности JavaScript-кода и не устраняет риск при XSS или вредоносном расширении браузера.
Есть и практический компромисс: почтовые сканеры могут автоматически открывать ссылки. Поэтому бездумное погашение токена уже при GET-запросе способно сделать ссылку недействительной до того, как её откроет пользователь. Часто первый переход только переводит токен в промежуточное состояние, а окончательная операция выполняется после явного действия пользователя и защищённого POST-запроса.
Сервис сброса пароля отправлял ссылку с токеном в URL. После перехода пользователь видел страницу с формой, на которой подключались сторонние аналитические скрипты. В журналах прокси и аналитической системе оказались полные адреса страниц, поэтому временные токены стали доступны большему числу компонентов, чем требовалось для сброса пароля.
Рассматривались три варианта. Полностью отказаться от токена в ссылке было неудобно для почтовых клиентов. Перенести токен во фрагмент URL уменьшало серверное журналирование, но требовало клиентского обмена и увеличивало последствия возможной XSS. Оставить query-параметр без изменений было проще всего, но сохраняло множество каналов утечки.
Выбрали промежуточную страницу без сторонних ресурсов: она принимает одноразовый токен, устанавливает ограниченное серверное состояние, сразу перенаправляет на чистый URL, а смену пароля выполняет через POST. Токен сделали короткоживущим и погашаемым только один раз, а query-параметры с ним исключили из прикладных и аналитических журналов. В результате открытие ссылки осталось совместимым с почтовыми клиентами, а срок и область действия утёкшего значения существенно сократились.
HTTPS защищает URL во время передачи между клиентом и конкретным TLS-терминатором, предотвращая его чтение при перехвате трафика. После расшифровки URL может попасть в журналы прокси, приложения и мониторинга, историю браузера или Referer. Поэтому HTTPS необходим, но не решает проблему вторичного распространения секрета.
Фрагмент обычно не отправляется серверу в HTTP-запросе, поэтому он не появляется в стандартных серверных access-журналах. Но для его обработки нужен клиентский код; токен становится доступен JavaScript, и XSS или небезопасный сторонний скрипт может его украсть. Это полезный механизм снижения одного класса утечек, а не универсальная замена одноразовому токену и строгой политике загрузки ресурсов.
Короткий срок ограничивает временное окно атаки, но в течение этого окна один и тот же токен может быть использован повторно. Одноразовое погашение предотвращает повторное применение уже использованного значения, однако должно выполняться атомарно: параллельные запросы не должны оба пройти проверку до фиксации факта использования. Даже вместе эти меры лишь уменьшают последствия утечки и не отменяют необходимость контролировать журналы, Referer и содержимое страницы.