JWT: типичные ошибки реализации и как их избежать
JWT часто принимают за готовую защиту: проверили подпись — значит, всё в порядке. Но неверный алгоритм, пропущенный claim или утёкший refresh-токен превращают удобную схему авторизации в источник уязвимостей и проблем в проде.

В JWT можно проверить подпись и всё равно принять чужой токен: например, если сервер доверяет алгоритму из заголовка или не проверяет, кому токен предназначен. Ошибки обычно не в формате, а в правилах проверки и обращении с токеном после выдачи. Разберём практику по RFC 7519 и рекомендациям RFC 8725: что проверять, где хранить и чем платить за разные подходы.
Сначала разделите три части токена
JWT — компактный формат передачи утверждений, а не шифрование и не готовая система авторизации. Подписанный JWT обычно состоит из заголовка, набора утверждений и подписи. Заголовок и содержимое можно декодировать без ключа: подпись помогает обнаружить изменение, но не скрывает данные.
Поэтому в payload не место паролям, секретам и персональным данным, которые нельзя раскрывать клиенту. Если содержимое нужно скрыть, одной подписи недостаточно: потребуется отдельное шифрование и корректное управление ключами. Для обычной авторизации чаще важнее не шифровать токен, а не помещать туда лишнее.
Подпись отвечает на вопрос «токен не меняли?», но не на вопросы «кому он выдан?», «не истёк ли?» и «можно ли использовать его здесь?».
Claim1 — поле с утверждением о токене или его владельце: например, срок действия или получатель.
Ошибки, которые пропускают проверку
Доверять алгоритму из самого токена
В заголовке JWT указано поле alg. Это не команда серверу выбрать любой поддерживаемый алгоритм. Сервер должен заранее задать разрешённый набор алгоритмов и отклонять токены с неподходящим значением. Проверка подписи должна учитывать ожидаемый алгоритм и тип ключа, а не просто вызывать универсальную проверку по данным из входящего токена.
Особенно опасно принимать неподписанный токен или смешивать режимы симметричной и асимметричной подписи без строгих правил. Проверьте поведение именно вашей библиотеки: какие алгоритмы разрешены по умолчанию, как она связывает ключ и алгоритм и что возвращает при несовпадении.
Проверять подпись, но забывать claims
Подпись не делает токен подходящим для любого API. При проверке обычно нужны как минимум срок действия и ожидаемые issuer и audience, если система их использует. Для конкретного действия дополнительно проверяйте права и контекст пользователя. Наличие роли в токене само по себе не означает, что эта роль всё ещё актуальна.
Если несколько сервисов принимают токены одного издателя, задайте для каждого ожидаемую аудиторию. Иначе токен, выпущенный для одного сервиса, может оказаться принят другим. Для разных типов токенов — например, доступа и смены пароля — используйте отдельные правила проверки, а не один универсальный обработчик.
Считать истечение срока отзывом
Claim срока действия ограничивает период, когда токен принимают, но не отзывает его мгновенно. Если токен украли, он может работать до истечения срока, если сервер не проверяет дополнительное состояние. Короткий срок действия уменьшает окно риска, но чаще требует обновления токенов и повышает нагрузку на процесс авторизации.
Refresh-токен даёт более долгий доступ и потому требует более строгого хранения и контроля. Для сценариев, где нужен немедленный отзыв, понадобится серверный механизм: например, список отозванных идентификаторов или проверка состояния сессии. Это добавляет хранение и запросы, то есть возвращает часть состояния, от которого JWT часто хотят избавиться.
Класть токен туда, откуда его легко украсть
Токен в localStorage доступен JavaScript на странице. При успешной XSS-атаке злоумышленник может прочитать его и использовать за пределами браузера. Cookie с флагами HttpOnly и Secure снижает риск чтения токена скриптом, но требует продумать защиту от CSRF. Универсально безопасного места хранения нет: выбор зависит от архитектуры приложения и модели угроз.
Не отправляйте токены в URL: адреса попадают в историю браузера, журналы и иногда в заголовок Referer. Не пишите их целиком в логи и сообщения об ошибках. Проверяйте также, не утекают ли токены в аналитику и инструменты мониторинга.
Сравните варианты хранения и отзыва
| Подход | Что выигрываете | Чем платите |
|---|---|---|
| Короткоживущий access-токен без серверного списка отзыва | Меньше запросов к хранилищу авторизации | Украденный токен действует до истечения срока; время жизни нужно выбирать по риску |
| Access-токен плюс refresh-токен | Можно обновлять короткий доступ без повторного входа | Нужно защищать refresh-токен, управлять его обновлением и отзывом |
| Проверка сессии или отзыва на сервере | Можно быстрее прекращать доступ | Появляется серверное состояние, запросы к нему и зависимость от доступности хранилища |
Это не рейтинг от хорошего к плохому. Если компрометация токена может дать доступ к критичным данным, удобство полностью stateless-проверки может не оправдать задержку с отзывом. Если доступ некритичный и срок токена короткий, дополнительный запрос на каждую операцию может оказаться неоправданным.
Рабочий чек-лист проверки
- Задайте правила на сервере. Зафиксируйте допустимые алгоритмы, ключи и назначение токена. Проверка: токен с неожиданным алгоритмом или типом отклоняется.
- Проверьте обязательные claims. Определите, какие поля нужны именно вашему сценарию, и сверяйте срок действия, издателя и получателя там, где они применимы. Проверка: отсутствующее или неподходящее значение приводит к отказу.
- Разделите типы токенов. Не используйте токен сброса пароля как access-токен и не применяйте одни правила проверки ко всем целям. Проверка: токен другого назначения не проходит в обработчик API.
- Опишите обновление и отзыв. Решите, что происходит при выходе, смене пароля и компрометации устройства. Проверка: команда понимает, когда токен перестаёт работать и где хранится состояние отзыва.
- Проверьте утечки. Просмотрите логи, URL, аналитику, клиентское хранилище и обработку ошибок. Проверка: тестовый токен не появляется в журналах и адресной строке.
Что ломается в пограничных случаях
- Расхождение часов. Серверы с разным временем могут по-разному трактовать границу срока действия. Синхронизируйте часы и задайте небольшой допустимый запас только осознанно.
- Ротация ключа. Новый ключ нужно распространить до того, как старый перестанет проверять ещё действующие токены. Продумайте, как проверяющий сервис выбирает ключ и что делает, если ключа нет.
- Изменение прав. Роль, записанная в токене, может устареть после изменения доступа. Решите, приемлемо ли это до истечения срока или права нужно сверять с сервером.
- Слишком много данных. Крупный токен увеличивает каждый запрос и может не пройти через ограничения прокси или сервера. Измерьте размер заголовков в реальном маршруте, включая балансировщик.
- Ошибки вместо отказа. Не превращайте некорректный токен в анонимный доступ к защищённому действию. Для закрытого ресурса ошибка проверки должна завершаться отказом.
Здесь рассматриваем проверку и жизненный цикл JWT; выбор конкретной библиотеки и настройка OAuth — отдельные темы. Точные названия параметров и безопасные значения зависят от версии используемой библиотеки: сверяйте их с её официальной документацией, а не переносите настройки из чужого примера.
Что делать дальше
Составьте таблицу для каждого типа токена: кто его выдаёт, кто принимает, какие claims обязательны, сколько он действует и как его отозвать. Затем добавьте негативные тесты: неверная подпись, неподдерживаемый алгоритм, истёкший токен, чужие issuer и audience, токен другого назначения. В проде следите за долей отказов проверки и размером заголовков, не записывая содержимое токенов в метрики и логи.
1 Claim — утверждение в payload JWT. Стандарт различает зарегистрированные, публичные и частные claims; название поля само по себе не гарантирует его смысл или достоверность.
Поиск источников: Реальные статьи и книги со ссылками, библиография по ГОСТ
Спросить за 15 ₽Источники: RFC 7519: JSON Web Token (JWT), RFC 8725: JSON Web Token Best Current Practices
Частые вопросы
Можно ли хранить JWT в localStorage?
Подписанный JWT зашифрован?
Нужно ли проверять audience и issuer?
Можно ли мгновенно отозвать уже выданный JWT?
Что делать, если библиотека принимает токен с неожиданным алгоритмом?
Безопасно ли передавать JWT в URL?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.