Глоссарий

Что такое JWT (JSON Web Token)? Как работают подписанные токены и как использовать их безопасно

JWT (JSON Web Token) — подписанный сервером токен, подделку которого можно обнаружить. Как он устроен и частые ошибки: нет проверки подписи, alg:none.

Опубликовано 2026-06-12 Обновлено 2026-10-06 8 мин чтения
На этой странице

Подписанный токен, который сервер выдаёт вам после входа, — это и есть JWT. Разберём, как он работает, и ключевые моменты безопасного использования (без шагов атаки).

Как это работает (три части)

JWT — это header.payload.signature, соединённые через .. Заголовок и полезная нагрузка просто закодированы через base64url (не зашифрованы); только подпись создаётся с помощью секретного (или общего) ключа.

1. header

метаданные вроде алгоритма подписи. читает кто угодно

2. payload

claims вроде id пользователя и срока действия. читает кто угодно = никаких секретов

3. signature

создаётся с помощью ключа. гарантирует отсутствие подделки

JWT состоит из трёх частей. Заголовок и полезная нагрузка «просто читаемы»; подпись даёт гарантию подлинности.

Ключевой момент — содержимое «просто читаемо». Поэтому полезная нагрузка — не место для паролей или персональных данных. Ценность JWT в том, что, проверив подпись, сервер может подтвердить «я выдал этот токен, и он не был изменён».

Меры для безопасного использования

1

Всегда проверяйте подпись и фиксируйте alg (самое важное)

Проверяйте подпись на сервере каждый раз. Зафиксируйте принимаемый алгоритм подписи (alg) на ожидаемом значении и отклоняйте alg:none (без подписи). Не доверяйте вслепую alg, записанному в заголовке токена.

RFC с лучшими практиками JWT (RFC 8725) требует, чтобы библиотеки позволяли вызывающему коду задать набор поддерживаемых алгоритмов и не использовали никаких других, а также чтобы все криптографические операции в JWT проверялись, а весь JWT отклонялся, если хоть одна из них не прошла.

2

Блокируйте смену алгоритма, а не только alg:none

Злоупотребление alg — это не только удаление подписи. RFC 8725 описывает, как замена RS256 на HS256 заставляет некоторые библиотеки проверять подпись через HMAC-SHA256, используя открытый ключ RSA как общий секрет (путаница алгоритмов). Открытый ключ по определению публичен — поэтому если это срабатывает, атакующий может сам создать действительную подпись.

Защита та же: не доверяйте alg из заголовка; фиксируйте алгоритм, который ожидает сервер. Сначала решите, какой алгоритм, и только потом проверяйте, а не выбирайте процедуру проверки по переданному вам ключевому материалу.

3

Проверяйте, для кого токен и до какого времени

Действительная подпись говорит лишь о том, что токен подлинный. Предназначен ли он вам и действует ли ещё — отдельная проверка.

RFC 7519 устанавливает, что если claim aud присутствует, а обрабатывающая сторона не находит в нём своего значения, JWT должен быть отклонён, а exp задаёт момент, начиная с которого JWT нельзя принимать к обработке (nbf работает так же с другого конца). RFC 8725 добавляет, что приложение должно проверять, что использованные ключи принадлежат издателю. Если ваш сервис принимает токен, законно выданный для другого сервиса, — вот как выглядит пропуск этих проверок.

4

Не кладите секреты в полезную нагрузку

Считайте, что декодировать содержимое может кто угодно. Держите секреты — пароли, персональные данные, ключи API — вне неё. Включайте только claims, которые не катастрофичны при просмотре, вроде идентификаторов и разрешений.

5

Держите короткий срок действия и имейте способ отзыва

Делайте токены доступа короткоживущими. Слишком долгий срок действия растягивает окно ущерба при краже токена. Если нужен отзыв, сочетайте короткоживущий токен доступа с управляемым на сервере refresh/сеансом.

6

Используйте надёжный ключ и храните его там, где его не украдут

Сделайте ключ подписи надёжным, не переиспользуйте его и храните в безопасном месте на сервере. Защищайте и сам токен — берегите его от кражи через XSS, носите в куке с безопасными атрибутами (→ остерегайтесь кражи через XSS).

Мнение этого сайта: JWT — не механизм сеансов на все случаи

JWT часто используют, чтобы «держать состояние входа», но у него есть слабость: отзыв затруднён. Если подпись действительна и токен не истёк, сервер по сути ему доверяет, поэтому сделать так, чтобы выход или аннулирование вступали в силу немедленно, неудобно.

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

Осторожно: «оно декодировалось» — не «оно действительно»

Поскольку содержимое читаемо, кто угодно может вставить JWT в декодер JWT и осмотреть заголовок и полезную нагрузку. Но декодирование (чтение содержимого) и проверка (подтверждение подлинности) — совершенно разные вещи. Возможность декодировать ничего не гарантирует о действительности. Что решает, подлинный ли токен, — это всегда проверка подписи на сервере. Читать содержимое для отладки удобно — просто не путайте «я смог прочитать» с «оно действительно».

JWT — лишь один из способов хранить состояние входа, и в одних случаях он подходит лучше серверного сеанса, а в других — хуже.

ПунктСерверный сеанс (cookie)JWT
Где хранится состояниеНа сервере (БД или память); в cookie только IDВнутри токена; сервер лишь проверяет подпись
Выход / блокировкаУдаление на сервере действует сразуПринимается до истечения срока, если отдельно не построить отзыв
Видимость содержимогоТолько ID, читать нечегоЛюбой может декодировать и прочитать
Где уместенОбычное веб-приложение на одном доменеНесколько сервисов или API проверяют один и тот же токен

Для веб-приложения, живущего на одном домене, серверного сеанса часто достаточно. Если же вы выбираете JWT, выдача нового токена при входе — тот же принцип, что и защита от фиксации сессии.

Реальный случай: библиотека, не проверявшая алгоритм

Смена алгоритма — не только теория. Широко используемая JWT-библиотека для Node.js jsonwebtoken в версиях 4.2.1 и ниже не проверяла должным образом алгоритм, указанный в заголовке токена, что позволяло обойти проверку (CVE-2015-9235, исправлено в 4.2.2). Токен, который должен был проверяться открытым ключом, можно было подписать алгоритмом с общим ключом (семейство HS), и он всё равно проходил — ровно та путаница алгоритмов, что описана выше.

Два урока:

  • Держите библиотеку обновлённой. Проверить зависимости на известные уязвимости можно, вставив lock-файл в сканер уязвимостей зависимостей
  • Не полагайтесь на значения библиотеки по умолчанию; явно указывайте принимаемый алгоритм в своём коде

Как проверить свои токены

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

  1. Совпадает ли alg в заголовке с ожидаемым (не none)?
  2. Есть ли exp, и подходит ли срок жизни от iat (момента выдачи) для этого применения?
  3. Есть ли aud и iss, и указывают ли они на ваш сервис?
  4. Нет ли в полезной нагрузке того, что вы не хотели бы показывать, — адресов почты, телефонов, ключей API?

Проверку на сервере проверяйте тестами в среде разработки. Токен с одним изменённым символом подписи, токен с другим alg, просроченный токен и токен для другого сервиса (неверный aud) должны отклоняться — держите это как автоматический тест.

Источники (первичные)

  • IETF RFC 8725, «JSON Web Token Best Current Practices» — datatracker.ietf.org (фиксация алгоритмов, проверка каждой криптографической операции, путаница алгоритмов, принадлежность ключей издателю)
  • IETF RFC 7519, «JSON Web Token (JWT)» — datatracker.ietf.org (нормативные определения exp, nbf и aud)
  • GitHub Advisory Database, «Verification Bypass in jsonwebtoken» (CVE-2015-9235) — github.com/advisories (затронуты 4.2.1 и ниже; исправлено в 4.2.2)
  • OWASP HTML5 Security Cheat Sheet — cheatsheetseries.owasp.org (не хранить идентификаторы сеанса в localStorage)

Читать дальше

FAQ

QЗашифровано ли содержимое JWT?
A

Нет. Заголовок и полезная нагрузка стандартного JWT «закодированы» через base64url, а не зашифрованы, поэтому любой, у кого есть токен, может прочитать содержимое. Поэтому никогда не кладите в полезную нагрузку секреты вроде паролей или персональных данных. JWT защищает не «конфиденциальность содержимого», а «то, что оно не было подделано» (целостность, через подпись).

QКакая настройка JWT самая опасная?
A

Не проверять подпись или принимать 'alg:none' (без подписи). Допустив это, вы позволяете злоумышленнику передать поддельный токен со свободно переписанным содержимым. Защита — всегда проверять подпись на сервере и фиксировать принимаемый алгоритм подписи (alg) на ожидаемом значении.

QДекодирование и проверка — это одно и то же?
A

Нет. Декодирование лишь читает содержимое, и это может сделать кто угодно (можно проверить декодером JWT). Проверка — это когда сервер с помощью секретного/открытого ключа подтверждает, что подпись действительна, токен не истёк, а издатель/аудитория верны. «Оно декодировалось» не означает «это действительный токен» — подлинность решает проверка на сервере.

QЧто использовать: JWT или сеансы (cookie)?
A

Для обычного веб-приложения на одном домене часто достаточно серверного сеанса с cookie, и выход или блокировка учётной записи вступают в силу сразу. JWT подходят, когда один и тот же токен проверяют несколько сервисов или API или когда вы получаете токены от внешнего поставщика идентификации (OAuth/OpenID Connect). Если используете JWT, держите короткий срок действия и сочетайте его с тем, что сервер может отозвать.

QМожно ли хранить JWT в localStorage?
A

Не рекомендуется. localStorage всегда доступен JavaScript, поэтому одна уязвимость XSS позволяет украсть токен. OWASP тоже не советует хранить идентификаторы сеанса в localStorage. В браузере безопаснее носить токен в cookie с HttpOnly, Secure и SameSite; если используете cookie, добавьте и защиту от CSRF.

QЧто такое «alg:none» в JWT?
A

Это JWT, в заголовке которого alg (алгоритм подписи) установлен в none, то есть подписи нет. Спецификация это допускает, но токен, используемый для аутентификации, например для входа, никогда не должен такое принимать — иначе кто угодно сможет создать токен с переписанным содержимым. Предотвращайте это, фиксируя на сервере принимаемый алгоритм на ожидаемом значении (например, только RS256).