Что такое JWT: компактный носитель утверждений
JSON Web Token (JWT) — это открытый промышленный стандарт RFC 7519 для компактной и автономной передачи данных между сторонами в виде JSON-объекта. Главное назначение JWT — подтверждение подлинности (аутентификация) и прав доступа (авторизация) пользователя в распределенных микросервисах и REST API.
Ключевое свойство классического JWT: он автономен (stateless). Серверу не требуется обращаться к общей базе данных сессий при каждом входящем HTTP-запросе. Вся необходимая контекстная информация (ID пользователя, роль, права и срок годности) уже «зашита» в тело токена, а математическая подпись гарантирует, что данные не были подделаны по пути.
Анатомия JWT: три части через точку
Любой корректный JWT токен представляет собой строку, состоящую из трех частей, разделенных точками:
header.payload.signature
Определяет метаданные токена: тип объекта (всегда "JWT") и алгоритм вычисления подписи. Например, симметричный HS256 (HMAC-SHA256) или асимметричный RS256 (RSA с публичным и приватным ключами).
{
"alg": "HS256",
"typ": "JWT"
} Содержит сами утверждения (claims) о пользователе и контексте сессии. Стандарт выделяет зарегистрированные (стандартные) и произвольные пользовательские поля.
{
"sub": "user_98412",
"name": "Alex Miller",
"role": "editor",
"iat": 1792929600,
"exp": 1792933200
} Математический хэш от склеенных заголовка и тела, создаваемый с секретным ключом. Вычисляется по формуле:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
your_secret_key
) Стандартные зарегистрированные Claims (RFC 7519)
Чтобы микросервисы и библиотеки понимали друг друга без разночтений, спецификация резервирует 7 общепринятых трехбуквенных полей:
| Claim | Полное имя | Назначение и пример |
|---|---|---|
| iss | Issuer | Издатель токена (например: https://auth.company.com). |
| sub | Subject | Идентификатор субъекта (уникальный ID пользователя или сервиса). |
| aud | Audience | Получатель токена. Сервер должен отклонить токен, если его имени нет в aud. |
| exp | Expiration Time | Время истечения токена в формате Unix Timestamp в секундах. После этого времени токен не валиден. |
| nbf | Not Before | Время, ранее которого токен не должен приниматься сервером. |
| iat | Issued At | Точное время создания токена. Служит для аудита и сброса старых сессий. |
| jti | JWT ID | Уникальный идентификатор самого токена (UUID). Используется для ведения черного списка отозванных токенов. |
5 критических ошибок безопасности при работе с JWT
1. Доверие заголовку «alg: none»
Классическая уязвимость реализации. Если бэкенд берет алгоритм верификации прямо из тела токена без строгой фильтрации, атакующий заменяет заголовок на {"alg": "none"}, стирает подпись и сервер принимает поддельный payload с ролью суперпользователя. На бэкенде алгоритм должен быть жестко зафиксирован в коде: jwt.verify(token, secret, { algorithms: ['HS256'] }).
2. Атака Key Confusion (RS256 vs HS256)
Если система использует асимметричный алгоритм RS256, открытый публичный ключ сервера доступен всем. Злоумышленник меняет в заголовке алгоритм на симметричный HS256 и подписывает поддельный токен... открытым публичным ключом! Незащищенная библиотека, получив токен, берет открытый ключ сервера в качестве секрета HMAC и успешно подтверждает фальшивую подпись.
3. Отсутствие валидации exp и iat
Никогда не полагайтесь на ручную распаковку Base64 без вызова проверки времени. Токен без поля exp становится вечным: если он утечет из сетевых логов или браузера, злоумышленник сохранит бессрочный доступ к аккаунту. Рекомендуемый срок жизни access-токена — от 5 до 15 минут.
4. Утечка токенов через сторонние онлайн-декодеры
Многие разработчики при отладке авторизации копируют боевые токены из консоли браузера и вставляют их в популярные веб-сервисы декодирования. Если сервис отправляет данные на удаленный сервер или пишет запросы в журналы веб-сервера, ваши токены с правами администратора и внутренними URL компании попадают в чужие руки.
Используйте только локальные декодеры. Наш инструмент JWT Декодер выполняет разбор Base64Url и инспекцию полей исключительно на клиенте в вашем браузере: никакие данные не покидают ваше устройство.
5. Уязвимость к XSS при хранении в localStorage
Код из localStorage доступен любому JavaScript-коду на странице. Одна уязвимая npm-библиотека или внедренный баннер может прочитать localStorage.getItem('token') и мгновенно слить его на удаленный сервер. Для веб-приложений токены безопаснее хранить в защищенных cookies с флагом HttpOnly.
Как быстро декодировать JWT в коде без тяжелых библиотек
Так как Header и Payload — это обычный Base64Url, их можно прочитать на клиенте одной функцией без установки тяжелых пакетов:
function parseJwtPayload(token) {
const base64Url = token.split('.')[1];
if (!base64Url) throw new Error('Некорректный формат JWT');
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
);
return JSON.parse(jsonPayload);
} Связанные инструменты Digital Vault PRO
- 👉 JWT Декодер и инспектор — мгновенно разложит токен на Header, Payload и Signature, подсветит истекшие claims и ошибки формата.
- 👉 Base64 кодировщик и декодер — перевод строк и файлов в Base64 и обратно по стандартам RFC 4648.
- 👉 Генератор криптостойких паролей — создание надежных 256-битных секретов для HMAC-подписи JWT.
Частые вопросы
Зашифрован ли JWT токен по умолчанию?
Нет. Стандартный JWT (JWS — JSON Web Signature) закодирован в Base64Url, а не зашифрован. Любой человек или прокси-сервер, получивший строку токена, может мгновенно декодировать Header и Payload и прочитать все находящиеся там claims (пользовательские данные, роли, ID). Для настоящего шифрования используется отдельный стандарт JWE (JSON Web Encryption).
Можно ли изменить данные в Payload и отправить токен на сервер?
Изменить данные в Base64Url можно за секунду, но сервер отклонит такой токен при первой же проверке. Подпись (Signature) рассчитывается на основе хэша заголовка и полезной нагрузки с использованием секретного ключа (HMAC) или приватного ключа (RSA/ECDSA). Любое изменение даже одного символа в Payload делает цифровую подпись недействительной.
В чём суть известной уязвимости alg: "none"?
В спецификации JWT существует специальный алгоритм "none", предназначенный для неподписанных токенов. Ранние или некачественные библиотеки проверки принимали токены, в заголовке которых злоумышленник указывал "alg": "none" и стирал подпись. Сервер доверял такому токену без валидации секрета, позволяя выдать себя за администратора. Современные библиотеки обязаны блокировать "alg": "none", если явно не настроен обратный режим.
Где безопаснее хранить JWT в браузере: в localStorage или Cookie?
Хранение в localStorage делает токен уязвимым для любой XSS-атаки: вредоносный JS-скрипт или скомпрометированная зависимость может украсть токен. Наиболее безопасный подход для веб-приложений — хранение refresh-токена в Cookie с флагами HttpOnly, Secure и SameSite=Strict/Lax, а access-токен держать исключительно в памяти приложения (in-memory variable).
Как отозвать (revoke) JWT токен до истечения срока exp?
JWT по своей природе децентрализован (stateless), поэтому сервер обычно не хранит сессии. Чтобы отозвать скомпрометированный токен, применяют три стратегии: 1) Делают короткий срок жизни access-токена (5–15 минут); 2) Ведут в Redis черный список отозванных токенов по идентификатору jti; 3) При смене пароля инкрементируют версию токена (tokenVersion) в профиле пользователя в базе данных.