Токен в URL после Referer: новые каналы утечки и защита веб-приложений

📋 Кратко

Заголовок Referer уже не передаёт полный URL стороннему сайту при стандартной политике браузера. Но токен всё равно остаётся в собственных журналах, истории браузера, аналитике, кэше и адресах внутренних ресурсов. Разбираем реальные каналы утечки и показываем, как безопасно строить OAuth, сессии и ссылки восстановления доступа.

⏱ 8 минут чтениясложность

Токен в URL выглядит как обычный параметр. Например, ссылка может содержать ?token=A1B2C3D4E5. На деле такая строка становится секретом, который браузер, сервер и вспомогательные системы обрабатывают по-разному.

Заголовок Referer не создаёт утечку сам по себе. Он сообщает адрес страницы, с которой пришёл запрос. При этом браузер может передать полный адрес своему источнику. Внешнему источнику он обычно передаёт только начало адреса.

Главный вывод: не размещайте секреты в URL. Политика Referrer-Policy снижает риск, но не убирает записи в журналах, истории, кэше и аналитике.
Referer сокращает риск, но не устраняет утечку
Referer сокращает риск, но не устраняет утечку

🔍 Почему формулировка про Referer вводит в заблуждение

Старый совет звучит так: токен в ссылке уйдёт через Referer на любой внешний ресурс. Сейчас это описание неполное. Современные браузеры сокращают межсайтовый заголовок до источника.

Подпись защищает целостность, не предотвращает копирование
Подпись защищает целостность, не предотвращает копирование

Стандартной политикой становится strict-origin-when-cross-origin. Chrome использует её по умолчанию с версии 85, выпущенной в августе 2020 года. Firefox переходит на неё в версии 87, выпущенной в марте 2021 года.

При запросе к своему ресурсу браузер может передать полный адрес. В нём остаются путь и параметры. При запросе к другому источнику браузер передаёт схему, хост и порт, но убирает путь и токен.

Старое поведение можно вернуть вручную через политику unsafe-url. Поэтому разработчик не должен рассчитывать только на настройки браузера. Приложение обязано не помещать секрет в адрес.

Что сделать сейчас: проверьте заголовок Referrer-Policy на страницах входа, восстановления доступа и OAuth.

📖 Что именно считается токеном

Веб-токен — это цифровой идентификатор доверия. Сервер выдаёт его после проверки пользователя. Затем клиент передаёт маркер при обращении к защищённым функциям.

Каждый системный слой создаёт новую копию секрета
Каждый системный слой создаёт новую копию секрета

Токеном бывает идентификатор сессии. Сервер создаёт сессию и обычно отправляет её идентификатор в cookie. Браузер хранит cookie и добавляет её к последующим запросам.

Другой вариант — JSON Web Token, или JWT. Это закодированная структура из заголовка, данных и подписи. Подпись помогает проверить неизменность токена. Она не делает токен безопасным при публикации в URL.

В OAuth применяются токены доступа и обновления. Access-токен обычно живёт от нескольких минут до часа. Refresh-токен действует дольше и позволяет получить новый access-токен.

К этой же группе относятся токены единого входа. Они могут быть представлены в виде JWT или утверждения SAML. Отдельный риск создают токены сброса пароля.

Важно: подпись защищает токен от подделки, но не от копирования. Получивший действующий токен злоумышленник может использовать его до окончания срока.

Что сделать сейчас: составьте список всех токенов приложения и отметьте, какие из них появляются в URL.

⚠️ Где токен оседает внутри своей системы

Первый канал — журналы веб-сервера. Веб-сервер обычно записывает путь запроса вместе с параметрами. Поэтому строка /reset?token=... остаётся в журнале полностью.

Короткий токен всё равно остаётся пригодным секретом
Короткий токен всё равно остаётся пригодным секретом

Эти записи затем копируются в обратный прокси, балансировщик и систему мониторинга. Их также могут забирать средства анализа событий. Каждый новый слой увеличивает число копий секрета.

Утечка возможна даже без внешнего запроса. В одном практическом сценарии сервер получает полный адрес от браузера при загрузке собственного ресурса. Внешний сервер получает только источник.

Проблема усиливается при широком доступе к журналам. Секрет может увидеть оператор, подрядчик или учётная запись с избыточными правами. Затем строка попадает в резервную копию или тестовую среду.

Логи нельзя считать безопасным хранилищем токенов. Их задача — помогать расследованию, а не сохранять секреты.

Что сделать сейчас: найдите параметры с названиями token, code, session и reset в журналах за последний период.

🧭 Как токен попадает к пользователю и аналитике

URL сохраняется в истории браузера. Пользователь также может добавить его в закладки или отправить коллеге. Восстановление доступа превращается в передачу полномочий.

Внутренние переходы расширяют поверхность утечки токена
Внутренние переходы расширяют поверхность утечки токена

Адрес может появиться в снимке экрана. Он может попасть в историю синхронизации браузера или в отчёт службы поддержки. Даже короткая ссылка остаётся секретом, если она открывает доступ.

Системы аналитики часто читают адрес текущей страницы. Это относится к собственным скриптам и сторонним компонентам. Параметры могут уйти в событие просмотра, отчёт или интерфейс аналитика.

Кэш браузера и промежуточные кэши тоже создают копии. Риск зависит от настроек и типа страницы. Но наличие секрета в адресе уже делает контроль сложнее.

Нельзя считать безопасным токен только потому, что он короткий. Случайная строка остаётся пригодной для повторного использования.

Что сделать сейчас: отключите запись полных URL на страницах восстановления и удалите секретные параметры из аналитических событий.

🛠️ Внешние ресурсы и внутренние переходы

Страница может загружать изображения, таблицы стилей и сценарии. Она также может обращаться к системам аналитики и рекламным компонентам. Такие запросы создают дополнительные точки контроля.

Безопасная архитектура важнее одной настройки заголовка
Безопасная архитектура важнее одной настройки заголовка

Межсайтовый Referer обычно содержит только источник. Но собственные ресурсы могут получить полный адрес. Поэтому разделение на «внешний» и «внутренний» риск не отменяет проблему.

Опасность появляется при переходе между внутренними сервисами. Один сервис может передать полный адрес другому. В результате токен увидят прокси, журнал и приложение, которым он не нужен.

Отдельный риск создают перенаправления. Если приложение принимает внешний адрес без проверки, токен может оказаться в цепочке переходов. Открытое перенаправление также облегчает отправку пользователя на чужой ресурс.

На страницах входа и восстановления доступа лучше не размещать сторонние компоненты. Это уменьшает число систем, которые видят адрес и события браузера.

Что сделать сейчас: составьте карту ресурсов на чувствительных страницах и удалите всё, что не нужно для их работы.

🔐 Referrer-Policy снижает риск, но не заменяет архитектуру

Referrer-Policy определяет, какую часть адреса браузер передаёт в заголовке Referer. Значение no-referrer запрещает передавать этот заголовок. Значение strict-origin-when-cross-origin оставляет полный адрес внутри источника и сокращает его между источниками.

При переходе с HTTPS на HTTP строгая политика не передаёт адрес. Это объясняет слово strict в названии. Политика полезна, но она не удаляет URL из истории, журналов и кэша.

Разработчик может задать политику для всего сайта. Её также можно уточнить для отдельной страницы или элемента. Настройка должна соответствовать чувствительности данных.

Политию нужно проверять вместе с реальными переходами. Важны запросы к своим ресурсам, внешним доменам и адресам после перенаправления.

Практическое правило: сначала уберите секрет из URL. Затем добавьте no-referrer или strict-origin-when-cross-origin как дополнительный барьер.

Что сделать сейчас: установите безопасную политику на чувствительные страницы и проверьте заголовок в браузере.

⚙️ Безопасный OAuth без токена в адресной строке

OAuth должен возвращать приложению код, а не access-токен. Код действует недолго и предназначен для обмена на серверной стороне. Сам access-токен не появляется в адресной строке.

Для открытого клиента используется authorization code flow с PKCE. PKCE связывает запрос авторизации и последующий обмен кода. Это снижает риск подмены или перехвата кода.

Параметр state защищает ответ от подмены. Приложение создаёт значение перед началом входа. После возврата оно сверяет значение с исходным запросом.

Вместо передачи данных через адрес можно использовать response_mode=form_post. Такой вариант отправляет ответ в теле формы. Он не делает весь процесс безопасным автоматически, но уменьшает видимость данных в URL.

Код должен использоваться один раз. После обмена сервер отклоняет повторную попытку. Access-токен получает короткий срок действия, а refresh-токен хранится отдельно и ротируется.

Что сделать сейчас: проверьте, возвращает ли ваш OAuth-провайдер код с PKCE и проверкой state, а не токен в URL.

🛡️ Сессии, cookie и токены восстановления

Идентификатор сессии лучше хранить в cookie. Для него подходят флаги HttpOnly и Secure. Первый ограничивает доступ сценариев к cookie. Второй требует защищённого соединения.

Параметр SameSite помогает контролировать отправку cookie между сайтами. Его значение выбирают с учётом сценариев входа и интеграций. Область cookie нужно ограничивать минимальным доменом и путём.

После входа приложение должно менять идентификатор сессии. Это защищает от фиксации сессии. Старый идентификатор нужно сделать недействительным.

Токен восстановления пароля нельзя делать постоянным. Сервер проверяет его срок, назначение и состояние. После использования токен становится недействительным.

Сервер также проверяет аудиторию токена. Он проверяет срок действия и подпись. Для чувствительных действий одного факта наличия строки недостаточно.

Что сделать сейчас: проверьте флаги cookie, смену идентификатора после входа и одноразовость токенов восстановления.

🧹 Как безопасно очистить адрес после обработки

Иногда приложение получает временный параметр после входа или восстановления доступа. Сначала сервер проверяет его. Затем приложение удаляет параметр из адресной строки.

Очистка не должна происходить до проверки. Иначе система потеряет данные для завершения операции. После успешной обработки браузер переходит на чистый адрес.

Для очистки используют безопасную навигацию. Страница не должна оставлять секрет в видимой строке после завершения обмена. Новый адрес должен содержать только необходимый путь.

Нужно очищать параметры перед внутренним перенаправлением. Иначе следующий сервис может получить старую строку. Это особенно важно для цепочек единого входа.

Очистка адреса не заменяет отзыв токена. Если строка уже попала в журнал, её считают раскрытой. Сервер ограничивает срок действия и блокирует повторное использование.

Что сделать сейчас: откройте тестовую ссылку с временным параметром и убедитесь, что после обработки адрес становится чистым.

📋 Content Security Policy и контроль компонентов

Content Security Policy ограничивает источники сценариев, изображений и других ресурсов. Политика снижает вероятность загрузки неожиданного компонента. Она также помогает контролировать страницы с чувствительными параметрами.

На страницах OAuth и восстановления доступа список источников должен быть минимальным. Сторонние сценарии и рекламные компоненты лучше исключить. Каждый внешний ресурс расширяет поверхность утечки.

Политика не скрывает URL от собственного сервера. Она не удаляет адрес из истории и журналов. Поэтому CSP работает вместе с отказом от токенов в query-параметрах.

Приложение отдельно проверяет перенаправления. Оно разрешает только заранее известные адреса. Пользователь не должен задавать произвольную цель перехода.

Нужно отслеживать изменения компонентов. Новый счётчик или сценарий может начать читать адрес страницы. Контроль списка ресурсов должен входить в проверку релиза.

Что сделать сейчас: включите строгую CSP для страниц входа и восстановления и проверьте все разрешённые источники.

📊 Практический чек-лист проверки утечки

Начните с поиска параметров в коде приложения. Проверьте ссылки входа, восстановления, приглашений и подтверждения операций. Отдельно изучите OAuth-перенаправления.

Затем проверьте серверные журналы. Ищите полные URL, параметры запроса и обращения к временным ссылкам. Проверьте журналы прокси, балансировщиков и систем аналитики.

Откройте чувствительную страницу с тестовым токеном. Загрузите собственный ресурс и внешний ресурс. Сравните полученные значения Referer и записи журналов.

Проверьте историю браузера, кэш и адрес после завершения операции. Убедитесь, что токен не попадает в события аналитики. Проверьте отсутствие внешних изображений и сценариев.

После теста отзовите тестовый токен. Не используйте рабочий секрет для проверки. Результаты занесите в список исправлений с ответственным владельцем.

  1. Убрать секреты из query-параметров.
  2. Перейти на код авторизации с PKCE.
  3. Добавить проверку state.
  4. Включить безопасную политику Referer.
  5. Очистить адрес после обработки.
  6. Сократить срок действия и отозвать токены.
  7. Закрыть доступ к журналам и аналитике.
Итог проверки: если тестовый токен виден в журнале, истории или аналитике, считайте канал подтверждённым. Меняйте схему передачи, а не только маскируйте значение.

Что сделать сейчас: проведите такой тест в отдельной среде и назначьте срок устранения каждого найденного канала.

📚 Читайте также

📖 Термины

OAuth · TLS · Безопасность приложений · Приватность · Трекинг

🔗 Источники