Токены доступа в веб-приложении: чек-лист защиты
Токен доступа часто крадут не из-за сложной криптографии, а из-за неверного хранения, широких прав и утечек в логах. Чек-лист поможет проверить выпуск, передачу, обновление и отзыв токенов в веб-приложении.

Украденный токен доступа превращает браузер или мобильное приложение в уже авторизованного клиента: злоумышленнику может не понадобиться пароль и второй фактор. Основные проблемы возникают не в алгоритмах шифрования, а в деталях — токен попадает в URL, сохраняется в небезопасном хранилище, живёт неделями или принимает запросы к чужому API. Ниже — практический чек-лист для приложений на OAuth 2.0 и связанных с ним схем, актуальный с учётом рекомендаций RFC 9700, опубликованных в 2025 году.
Сначала определите, какой токен вы защищаете
Один и тот же термин используют для разных объектов. Access token предъявляют API, чтобы получить доступ к ресурсу. Refresh token нужен клиенту, чтобы запросить новый access token без повторного входа. Они не должны иметь одинаковый срок жизни и одинаковые права.
Если приложение только устанавливает собственную сессию между браузером и сервером, вам может быть не нужен OAuth вообще. В этом случае обычно достаточно защищённой cookie-сессии. OAuth 2.0 нужен там, где один компонент выдаёт токен, а другой проверяет его и предоставляет доступ к API.
Термин Bearer-токен означает токен предъявителя: сервер считает запрос авторизованным, если у отправителя есть действительное значение токена. Поэтому утечка такого значения фактически равна временной краже полномочий.
Чек-лист выпуска и состава токена
- Сформулируйте аудиторию токена. API должно проверять, что токен предназначен именно ему, а не другому сервису вашей системы. Для JWT это обычно проверка поля
aud, если оно используется вашим протоколом и библиотекой. Для непрозрачных токенов аудитория должна быть связана с записью на стороне сервера. - Ограничьте область доступа. Выдавайте минимальные scopes или права: например, чтение профиля отдельно от изменения платёжных реквизитов. Не выдавайте административное право каждому токену после обычного входа.
- Проверьте срок действия. Access token должен жить столько, сколько нужно конкретному сценарию, а не «на всякий случай». Точное значение зависит от риска, архитектуры и требований к повторной авторизации; фиксированного универсального срока для всех приложений нет.
- Не кладите секреты в payload без необходимости. Подписанный JWT защищает целостность, но его содержимое обычно можно прочитать. Персональные данные, пароли и ключи шифрования в payload не место.
- Проверяйте алгоритм и ключ. Сервер не должен принимать алгоритм только потому, что клиент указал его в заголовке. Разрешённый алгоритм и доверенный ключ задаются конфигурацией сервера, а не входным запросом.
- Разделяйте токены по средам и клиентам. Ключи разработки, теста и продакшена не должны совпадать. Отдельно регистрируйте web-клиент, мобильное приложение и внутренний сервис.
После каждого пункта проверьте не только успешный запрос, но и отказ: запрос с чужой аудиторией, просроченным сроком, лишним scope и изменённой подписью должен завершаться контролируемой ошибкой, а не ответом с данными.
Безопасный токен — это не просто длинная случайная строка. Это строка с ограниченным сроком, минимальными правами, понятным получателем и возможностью обнаружить её повторное использование.
Выберите хранение под модель угроз
В браузере спор обычно сводится к двум вариантам. Хранение access token в localStorage удобно для JavaScript-клиента, но любой успешно выполненный вредоносный скрипт на origin сможет его прочитать. Cookie с флагом HttpOnly недоступна JavaScript, однако браузер отправляет её автоматически, поэтому нужно отдельно защищаться от CSRF.
| Подход | Что выигрываете | Цена и риск | Когда рассматривать |
|---|---|---|---|
| HttpOnly Secure cookie | Токен нельзя прочитать обычным JavaScript | Нужны защита от CSRF, корректные SameSite и CORS | Серверное веб-приложение и same-site сценарий |
| Память вкладки или приложения | Токен исчезает после закрытия контекста, меньше постоянных следов | После перезагрузки нужен новый обмен или обновление | SPA с короткоживущим access token |
| localStorage | Простая интеграция и сохранение между перезагрузками | Успешный XSS может прочитать токен и отправить его наружу | Только после оценки риска и при отсутствии более безопасной схемы |
| Сессионный backend-for-frontend | Браузер не видит токен API, сервер хранит интеграционные секреты | Дополнительный серверный слой и контроль его сессий | Критичные кабинеты и приложения с несколькими API |
Для cookie задайте Secure, чтобы она уходила только по HTTPS, и HttpOnly, чтобы клиентский JavaScript не мог её прочитать. Значение SameSite выбирайте по реальному сценарию переходов между сайтами; слишком строгая настройка может сломать авторизацию, слишком мягкая увеличит требования к CSRF-защите.
Если токен всё же хранится в браузерном JavaScript-хранилище, не считайте это защитой от XSS. Нужны экранирование вывода, политика CSP, контроль сторонних скриптов, обновление зависимостей и запрет вставки непроверенного HTML.
Проверьте передачу токена
Только заголовок, не URL
Для Bearer-токена используйте заголовок Authorization с HTTPS. Не передавайте секрет в query-параметре: URL попадает в историю браузера, access-логи прокси, системы аналитики и заголовок Referer при переходах.
Проверьте также редиректы. Если запрос с токеном уходит на другой host или схема меняется с HTTPS на HTTP, клиент и прокси не должны пересылать туда заголовок авторизации.
Логи и трассировка
В логах не должно быть полного значения access token, refresh token, cookie и заголовка Authorization. Маскируйте секреты до записи в лог, а не при последующем экспорте. То же относится к ошибкам, дампам запросов, APM, системам поддержки и скриншотам из админки.
- Проверьте access-логи веб-сервера и reverse proxy.
- Проверьте телеметрию HTTP-клиента и трассировки.
- Проверьте тела исключений и диагностические дампы.
- Проверьте, не отправляет ли фронтенд токен в аналитику или сторонние виджеты.
Сделайте обновление и отзыв проверяемыми
Refresh token нельзя считать бессрочным пропуском. Ограничьте его срок, храните безопаснее access token и предусмотрите отзыв при выходе, смене пароля, блокировке учётной записи или подозрении на компрометацию.
Для публичных клиентов полезна ротация refresh token: при обмене старое значение заменяется новым, а повторное использование старого обнаруживается и приводит к отзыву соответствующей цепочки. Это не отменяет защиту от кражи, но уменьшает окно незаметного повторного использования.
Сервису нужно уметь ответить на вопрос: «Что произойдёт, если этот токен украли прямо сейчас». Если единственный ответ — «он истечёт через месяц», срок слишком велик или отсутствует рабочий механизм отзыва.
Что ломается чаще всего
- Проверяют подпись, но не срок. Валидная подпись не делает просроченный токен действующим. Проверяйте срок действия и момент начала действия, если эти поля применяются в вашей схеме.
- Принимают любой scope. Сервис проверяет, что токен существует, но не проверяет право на конкретную операцию. Проверка должна учитывать HTTP-метод, ресурс и действие.
- Считают logout отзывом. Удаление cookie на одном устройстве не обязательно отзывает уже выданный токен. Разведите локальный выход, серверный отзыв и глобальную блокировку пользователя.
- Смешивают окружения. Тестовый frontend обращается к продакшен-API с теми же ключами. Разделите client ID, ключи, issuer и разрешённые redirect URI.
- Разрешают слишком широкий CORS. CORS не заменяет аутентификацию и не должен без причины разрешать произвольные origins вместе с credentials.
Когда выбирать какой подход
Для обычного серверного сайта начните с cookie-сессии или backend-for-frontend, а не с самостоятельной реализации OAuth в браузере. Для SPA, которому нужен отдельный API, используйте проверенный OAuth-клиент и короткоживущий access token; конкретный вариант хранения выбирайте после оценки XSS и CSRF.
Если несколько сервисов принимают токены, заранее опишите issuer, audience, scopes, алгоритмы и порядок отзыва. Если токен нужен только одному внутреннему запросу, не превращайте его в универсальный ключ ко всей платформе. Инвалидация кешей, управление ключами подписи и федерация между организациями — отдельные темы; здесь важны границы самого токена.
Что сделать дальше
Составьте таблицу всех токенов в системе: кто выпускает, кто проверяет, где хранится, какие права даёт, сколько живёт и как отзывается. Затем добавьте автоматические проверки для просроченного токена, неверной аудитории, лишнего scope, повторного refresh token и запроса по HTTP.
В продакшене начните измерять не количество успешных логинов, а события риска: отказы по причине просрочки, повторное использование refresh token, запросы с неизвестным issuer, обращения к запрещённым scopes и попадание секретов в логи. После этого отдельно проверьте документацию вашей OAuth-библиотеки и настройте ротацию ключей подписи без остановки API.
Тендерный специалист 44-ФЗ / 223-ФЗ: Разбор PDF-документации закупки, поиск «заточек» под поставщика, чек-лист заявки.
Спросить за 20 ₽Источники: OAuth 2.0 Security Best Current Practice, RFC 9700, The OAuth 2.0 Authorization Framework: Bearer Token Usage, RFC 6750
Частые вопросы
Можно ли хранить access token в localStorage?
Достаточно ли HTTPS, чтобы защитить токен?
Как долго должен жить access token?
Нужно ли отзывать access token при logout?
Что делать, если токен попал в лог?
Нужно ли использовать JWT вместо случайной строки?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.