Кладовочка

О разном, чтобы было под рукой онлайн

JWT — что это, структура, как декодировать

Что такое JWT

JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа. Токен представляет собой компактную строку из трёх частей, разделённых точками: заголовок, полезная нагрузка и подпись. JWT позволяет безопасно передавать данные между двумя сторонами в формате JSON — например, между сервером и клиентом, между микросервисами, или в составе OAuth 2.0.

Ключевое отличие JWT от обычных сессионных токенов — самодостаточность. Токен содержит всю необходимую информацию о пользователе или разрешениях прямо внутри себя. Серверу не нужно обращаться к базе данных, чтобы проверить, кто перед ним — достаточно проверить подпись.

Структура JWT

JWT-токен выглядит как строка из трёх Base64URL-закодированных блоков, разделённых точками:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

Заголовок (Header)

Первая часть токена содержит метаинформацию о самом токене — как минимум два поля:

{
  "alg": "HS256",
  "typ": "JWT"
}

alg — алгоритм подписи (HS256, RS256, ES256 и другие). typ — тип токена, почти всегда "JWT". Заголовок кодируется в Base64URL.

Полезная нагрузка (Payload)

Вторая часть содержит утверждения (claims) — данные, которые мы хотим передать. Существует три типа утверждений:

  • Registered claims — стандартные поля: iss (issuer, кто выпустил), sub (subject, о ком), aud (audience, для кого), exp (expiration, срок истечения), iat (issued at, когда выпущен), nbf (not before, недействителен до).
  • Public claims — открытые, зарегистрированные в IANA: name, email, role и другие.
  • Private claims — любые кастомные поля, согласованные между сторонами.
{
  "sub": "1234567890",
  "name": "Иван Петров",
  "role": "admin",
  "iat": 1718123456,
  "exp": 1718209856
}

Важный момент: payload не шифруется, а только кодируется. Любой, у кого есть токен, может раскодировать Base64URL и прочитать содержимое. Никогда не кладите в JWT пароли, номера карт и другие секретные данные.

Подпись (Signature)

Третья часть — криптографическая подпись, которая гарантирует, что токен не был изменён. Подпись создаётся так:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

Если злоумышленник изменит роль в payload с "user" на "admin", подпись перестанет совпадать, и сервер отклонит токен. Но это работает только при правильной проверке подписи на сервере.

Алгоритмы подписи

HS256 (HMAC с SHA-256) — симметричный алгоритм. Один и тот же секретный ключ используется и для создания, и для проверки подписи. Прост в реализации, подходит для монолитов и внутренних микросервисов. Минус: если ключ утёк, злоумышленник может создавать токены от имени любого пользователя.

RS256 (RSA с SHA-256) — асимметричный алгоритм. Приватный ключ создаёт подпись, публичный — проверяет. Чаще всего используется в OAuth 2.0 и OpenID Connect. Плюс: даже если публичный ключ известен всем, подделать токен без приватного ключа невозможно. Минус: сложнее в настройке, ключи нужно ротировать.

ES256 (ECDSA с SHA-256) — современный асимметричный алгоритм на эллиптических кривых. Ключи компактнее, операции быстрее, чем RSA. Рекомендуется для новых проектов.

None — алгоритм без подписи. Существует формально, но использовать его в production категорически нельзя. Многие библиотеки по умолчанию блокируют alg: "none".

Как декодировать JWT

Декодировать JWT можно несколькими способами.

В командной строке (Linux/Mac) — декодируем Base64URL через base64 и jq:

# Берём вторую часть токена (payload)
echo "eyJzdWIiOiIxMjM0NTY3ODkwIn0" | base64 -d | python3 -m json.tool

На сайте jwt.io — скопируйте токен в поле, и сайт покажет расшифрованные header и payload, а также проверит подпись, если ввести ключ.

В коде — практически каждый язык программирования имеет библиотеки для работы с JWT:

  • JavaScript/Node.js: jsonwebtoken — jwt.verify(token, secret)
  • Python: PyJWT — jwt.decode(token, key, algorithms=["HS256"])
  • Java: jjwt — Jwts.parser().setSigningKey(key).parseClaimsJws(token)
  • Go: golang-jwt/jwt — jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {...})

Важно: всегда проверяйте подпись, срок действия (exp) и issuer (iss). Не полагайтесь только на декодирование payload — любой может создать токен с любым содержимым.

Где используются JWT

Аутентификация — после входа пользователя сервер выдаёт JWT, клиент сохраняет его в localStorage или cookie, и при каждом запросе прикрепляет заголовок Authorization: Bearer <token>. Сервер проверяет подпись и извлекает user_id из payload.

Микросервисы — сервис А создаёт JWT с правами пользователя и передаёт его сервисам B и C. Каждый сервис проверяет подпись, не обращаясь к центральному серверу авторизации.

Одноразовые ссылки — письмо со ссылкой для сброса пароля содержит JWT с коротким сроком жизни. Сервер проверяет токен и идентифицирует пользователя без обращения к базе.

Mobile-backend — приложение получает JWT при логине и хранит его в защищённом хранилище (Keychain/KeyStore). Токен живёт дольше сессии браузера.

Безопасность

  • Короткий срок жизни access-токенов (15-60 минут) + refresh-токены для обновления.
  • Хранение на клиенте: для веба — httpOnly cookie (не localStorage, он доступен для XSS-атак), для мобильных — Keychain/KeyStore.
  • Проверка alg: никогда не доверяйте заголовку alg без проверки. Некоторые библиотеки позволяли подменить RS256 на HS256, используя публичный ключ как секрет.
  • Чёрные списки: если пользователь вышел, а токен ещё не истёк, сервер должен иметь возможность отозвать токен. Ведите список отозванных JWT или используйте короткий срок жизни.
  • Не храните секреты в payload: пароли, ключи, платёжная информация — только на сервере.
  • Используйте HTTPS: JWT передаётся в открытом виде — без TLS его можно перехватить и использовать.

Попробовать инструмент

Чтобы на практике разобраться со структурой JWT, попробуйте наш декодер JWT на txtly.ru, где можно вставить токен и посмотреть все три его части в удобном интерфейсе — без кода и командной строки.