belun.app Блог
EN

Как декодировать JWT и что на самом деле значит каждый claim

Практическое руководство по JSON Web Token: три части, стандартные claim'ы и как безопасно прочитать токен, не сливая его.

Разработчик разбирает JSON Web Token на ноутбуке во время отладки входа в систему

Когда на вас падает баг с авторизацией, обычно всё сводится к одному: вы смотрите на длинную строку, которая начинается с eyJ. Это JSON Web Token, и быстрее всего понять, почему запрос отклоняют, — прочитать, что внутри. Вставьте токен в JWT-декодер, и заголовок с payload откроются как обычный JSON, без командной строки.

Три части через точку

JWT — это три строки Base64URL, склеенные точками: header.payload.signature.

Заголовок маленький, обычно два поля. alg говорит, как токен подписан (чаще всего HS256 или RS256), а typ почти всегда равен JWT. В payload лежит самое интересное: кому выдан токен, когда выдан, когда истекает. Подпись — та часть, которая подтверждает, что первые два куска никто не правил после подписания.

Вот что часто упускают: заголовок и payload не зашифрованы. Они закодированы. Base64URL разворачивается обратно кем угодно — поэтому декодер показывает всё без ключа. Если вы хоть раз скидывали JWT в чат с вопросом «почему он не работает», вы отдали собеседнику все claim’ы из него.

Читаем стандартные claim’ы

Спецификация JWT (RFC 7519) задаёт несколько зарегистрированных claim’ов с короткими непонятными именами. Они встречаются постоянно, так что их стоит знать наизусть:

  • iss — кто выпустил токен;
  • sub — субъект, обычно ID пользователя;
  • aud — для какого сервиса токен предназначен;
  • exp — время истечения в виде Unix-timestamp;
  • iat — время выпуска, тоже Unix-timestamp;
  • nbf — «не раньше»: до этого момента токен недействителен.

Три временные метки сбивают с толку, потому что 1719230400 ни о чём не говорит на глаз. Декодер переводит их в читаемые даты UTC, и вы сразу видите: exp наступит через пять минут или истёк пять месяцев назад.

Именно exp вы будете проверять чаще всего. Если текущее время больше него, токен мёртв, и сервер его отклоняет — отсюда добрая половина тикетов в духе «вчера же работало». А когда часы на двух серверах разъезжаются, один может считать токен живым, а другой — просроченным.

Декодировать — не значит проверить

Об этом стоит сказать громко. Чтение токена показывает, что он утверждает. Оно не говорит, правда ли это.

Проверка — это пересчёт подписи с секретом (для HMAC) или открытым ключом (для RSA и ECDSA) и сверка результата. Она должна происходить на сервере, с ключом, который браузер никогда не видит. Клиентский декодер вроде нашего намеренно останавливается на расшифровке: он показывает сегмент подписи, но не делает вид, что её проверил.

История тут показательная. Некоторые ранние библиотеки JWT принимали заголовок alg: none и вообще пропускали проверку подписи — это позволяло подделывать токены, просто удалив подпись. Современные библиотеки так не делают, но вывод остаётся: payload, расшифрованный в браузере, ничего не доказывает.

Держите токен у себя

Многие онлайн-декодеры отправляют ваш токен на свой backend. Это плохо, когда токен — живая сессия реального пользователя: вы только что слили рабочие учётные данные постороннему сервису.

JWT-декодер выполняет всё в JavaScript прямо на странице. Ничего не загружается. Можно выдернуть сетевой кабель после загрузки страницы — декодирование продолжит работать. Для токенов со staging или production это не приятный бонус, а единственный безопасный способ их посмотреть.

Вставьте токен, прочитайте claim’ы, проверьте срок действия — и возвращайтесь к делам.

Попробуйте инструмент

JWT-декодер →