Как декодировать JWT
- Вставьте токен в поле Токен. Подойдёт целая строка
Authorization: Bearer eyJ…, а также токен в кавычках или разбитый на несколько строк. - Посмотрите результат. Три части выделены разными цветами, заголовок и полезная нагрузка отформатированы, а в разделе Клеймы перечислены все клеймы с пояснениями. Время в
expиiatпоказано в местном времени, в UTC и относительно, например «истекает через 1 час». - Чтобы проверить подпись, вставьте общий секрет или открытый ключ в разделе Проверка подписи. Название поля зависит от
algтокена: Секрет для HS256/384/512 и Открытый ключ для всего остального. - Прочитайте предупреждения. Инструмент отмечает
alg: none, истёкшие токены,nbfв будущем, отсутствиеexp, время в миллисекундах и заголовки, которые ссылаются на удалённые ключи (jku,x5u) или несут собственный ключ (jwk).
Токен можно открыть и сразу по ссылке вида /jwt-decoder/#token=eyJ…. Часть после # называется фрагментом URL, и браузеры никогда не отправляют её на сервер.
Что внутри токена
Подписанный JWT (JWS в компактной форме) состоит из трёх строк Base64URL, соединённых точками: header.payload.signature. Вот настоящий токен HS256, выпущенный 1 января 2026 года и действующий один час:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyLTQyIiwiYXVkIjoiYXBpLmV4YW1wbGUuY29tIiwiaWF0IjoxNzY3MjI1NjAwLCJleHAiOjE3NjcyMjkyMDB9.U3Gb12hN5Fnpi1rJ7nrkUzIEidkg-hPnbgiIpTah3R4
Первая часть декодируется в заголовок {"alg":"HS256","typ":"JWT"}. Вторая содержит полезную нагрузку:
{"iss":"https://auth.example.com","sub":"user-42","aud":"api.example.com","iat":1767225600,"exp":1767229200}
Третья часть содержит 32 байта HMAC-SHA256, вычисленные по первым двум частям, поэтому изменение любого символа в заголовке или нагрузке её ломает. По этой же причине почти каждый JWT начинается с eyJ: это Base64 от {". Части используют URL-safe алфавит (- и _) без дополнения =; в статье как JWT использует Base64URL токен разбирается вручную.
JWS или JWE: подписанные и зашифрованные токены
Большинство JWT относятся к JWS: они подписаны, и прочитать их может кто угодно. JWE зашифрован и состоит не из трёх частей, а из пяти: header.encrypted_key.iv.ciphertext.tag. Его заголовок остаётся читаемым и называет алгоритм управления ключом (alg, например RSA-OAEP-256 или dir) и шифр содержимого (enc, например A256GCM).
Если вставить сюда JWE, инструмент декодирует этот заголовок, покажет alg, enc и kid и пометит полезную нагрузку как зашифрованную. Для расшифровки нужен закрытый ключ получателя или общий ключ, а расшифровывать JWE инструмент не умеет. Если пользователи не должны читать полезную нагрузку токена, нужен JWE. Секреты в нагрузке JWS всё равно что записаны открытым текстом. Подробнее об этом в статье Base64 – это не шифрование.
Справочник клеймов JWT
В первых семи строках зарегистрированные клеймы из RFC 7519, остальные часто встречаются в токенах OpenID Connect и OAuth. Все значения времени записываются как NumericDate: целые секунды с 1970-01-01 UTC, никогда не миллисекунды.
| Клейм | Название | Что проверять |
|---|---|---|
iss | Издатель | Кто создал и подписал токен. Должен совпадать с издателем, которому вы доверяете. |
sub | Субъект | Пользователь или сущность, к которой относится токен, часто ID пользователя. |
aud | Аудитория | Для кого предназначен токен. Строка или массив; ваш API должен отвергать токены, адресованные кому-то другому. |
exp | Время истечения | После этого момента токен нужно отвергать. |
nbf | Не ранее | До этого момента токен ещё недействителен. |
iat | Время выдачи | Когда токен выдан. Полезно для ограничения возраста. |
jti | JWT ID | Уникальный идентификатор, чтобы обнаруживать повторное использование или отзывать отдельный токен. |
azp | Авторизованная сторона | OAuth-клиент, которому выдан токен (OpenID Connect). |
scope, scp | Скоупы | Выданные OAuth-скоупы; scp используют Microsoft и Okta. |
nonce | Nonce | Привязывает ID-токен OpenID Connect к запросу на вход, который его запросил. |
auth_time | Время аутентификации | Когда пользователь на самом деле вошёл; это может быть задолго до iat. |
roles, groups | Роли, группы | Данные авторизации, которые добавляет провайдер идентификации. |
tid, oid | Тенант, ID объекта | Идентификаторы тенанта и объекта пользователя в Microsoft Entra ID. |
У заголовка свои параметры: alg (алгоритм подписи), typ (обычно JWT или at+jwt для access-токенов OAuth), kid (каким ключом подписан) и cty (равен JWT, когда один токен вложен в другой).
Проверка подписи: какой ключ вставлять
Откуда на самом деле пришёл токен, показывает только проверка подписи, а нужный для неё ключ зависит от семейства алгоритма в заголовке.
alg | Какой ключ вставить | Длина подписи |
|---|---|---|
| HS256, HS384, HS512 | Общий секрет. Выставьте Формат секрета в «Текст (UTF-8)», «Base64 / Base64URL» или «Hex» в зависимости от того, как он хранится; подойдёт и JWK типа oct. | 32, 48, 64 байта |
| RS256–RS512, PS256–PS512 | Открытый ключ RSA: PEM BEGIN PUBLIC KEY, PEM BEGIN RSA PUBLIC KEY, сертификат X.509, JWK или JWKS. | По размеру ключа; 256 байт для RSA-2048 |
| ES256, ES384, ES512 | Открытый ключ EC на P-256, P-384 или P-521 в любом из перечисленных видов. | 64, 96, 132 байта («сырой» r‖s) |
| EdDSA | Открытый ключ Ed25519 в PEM или JWK типа OKP. | 64 байта |
У токенов от провайдера идентификации открытые ключи лежат по адресу jwks_uri, указанному в https://<issuer>/.well-known/openid-configuration. Сама страница загрузить этот URL не может: подключаться к другим серверам ей запрещено. Откройте его в новой вкладке и вставьте JSON. Из JWKS ключ выбирается по kid; если kid в токене нет, перебираются все подходящие ключи.
Подписи ECDSA в формате DER (для ES256 обычно 70–72 байта вместо 64) инструмент распознаёт, конвертирует и помечает: JWS требует «сырой» r‖s, и многие библиотеки DER отвергают. Вставленный закрытый ключ тоже сработает (открытая половина вычисляется из него), хотя для проверки он никогда не нужен. ES256K и Ed448 не поддерживаются: их нет в WebCrypto.
Если токен HS256 не проходит проверку с секретом, в котором вы уверены, часто оказывается, что секрет хранится в конфиге в Base64, и сервер декодирует его перед использованием. Переключите Формат секрета на Base64 и попробуйте снова.
Почему декодировать токен здесь безопасно
Пока не истёк срок, JWT из заголовка Authorization работает как учётные данные. Если вставить живой продакшен-токен на веб-страницу, которая отправляет его на сервер, владелец этого сервера сможет повторно использовать его против вашего API, а снаружи обычно не понять, делает ли страница такое.
На этой странице и декодирование, и проверка подписи через WebCrypto выполняются в браузере. Кроме того, у страницы есть политика безопасности контента, в которой connect-src разрешает только /cdn-cgi/rum (там Cloudflare Web Analytics считает просмотры страниц), поэтому все остальные запросы fetch, XHR, WebSocket и beacon, какие страница могла бы попытаться отправить, браузер блокирует. Это легко проверить самостоятельно: откройте инструменты разработчика на вкладке Network и вставьте токен или отключите Wi-Fi после загрузки страницы и продолжайте декодировать.
И всё же лучше пользоваться тестовыми токенами или токенами, срок которых вот-вот истечёт, а если продакшен-токен оказался вставлен куда-то, что вы не контролируете, отзовите сессию.
Истёкшие токены, nbf и расхождение часов
exp и nbf сверяются с часами проверяющей стороны, а часы расходятся. Токен от сервера, чьи часы спешат на 30 секунд, может прийти с iat и nbf в будущем и быть отвергнут как «ещё не действующий». Поэтому библиотеки JWT допускают небольшой запас: leeway в PyJWT, clockTolerance в jsonwebtoken и jose.
Этот декодер сверяется с часами вашего устройства и говорит об этом простыми словами: «истёк 2 дня назад» для exp, «ещё не действует» с оставшимся временем для nbf. Если iat в будущем, появится предупреждение: дело в часах издателя. Значение вроде 1767229200000 помечается как миллисекунды: если читать его как секунды, оно окажется на десятки тысяч лет в будущем. Это частая ошибка, когда в коде используют Date.now() вместо Math.floor(Date.now() / 1000).
Чтобы прочитать клеймы в коде без проверки, хватит стандартной библиотеки Python:
import base64, json
def b64url_decode(part):
return base64.urlsafe_b64decode(part + "=" * (-len(part) % 4))
header_b64, payload_b64, signature_b64 = token.split(".")
print(json.loads(b64url_decode(payload_b64)))
В Node.js то же самое делает JSON.parse(Buffer.from(payload_b64, "base64url").toString("utf8")). Для решений об авторизации используйте библиотеку, которая проверяет подпись и жёстко задаёт допустимые алгоритмы. Больше фрагментов кода есть на страницах про Python и Node.js.
Чтобы собрать или переподписать токен для тестов, воспользуйтесь кодировщиком JWT. Отдельную часть токена можно декодировать декодером Base64URL.
Вопросы и ответы
Можно ли декодировать JWT без секретного ключа?
Да. Заголовок и полезная нагрузка содержат обычный JSON в Base64URL, так что прочитать их может любой, у кого есть токен. Секрет или открытый ключ нужен только для того, чтобы проверить подлинность подписи.
Почему декодер пишет, что подпись неверна?
Обычно ключ не тот или не в том виде. Для HS256 проверьте, не хранится ли секрет в Base64 или hex, и выставьте Формат секрета соответственно. Для RS256 или ES256 убедитесь, что открытый ключ соответствует kid токена; если вставить JWKS целиком, инструмент сам выберет ключ по kid.
Как узнать, когда истекает JWT?
Посмотрите на клейм exp. Это число секунд с 1970-01-01 UTC, а таблица клеймов показывает его в местном времени, в UTC и относительно, например «истёк 2 дня назад». Токен без exp сам по себе никогда не истекает.
Может ли этот инструмент расшифровать JWE?
Нет. Для зашифрованного токена из пяти частей он декодирует защищённый заголовок и показывает alg, enc и kid. Расшифровка полезной нагрузки требует закрытого или общего ключа получателя, а этот инструмент её не выполняет.
Отправляется ли куда-нибудь токен, когда я его вставляю?
Нет. Декодирование и проверка выполняются в браузере средствами JavaScript и WebCrypto, а политика безопасности контента страницы блокирует все сетевые запросы, кроме счётчика просмотров Cloudflare.