Аутентификация только что сломалась в продакшене. Пользователи получают ошибки «Invalid Token», и вам нужно быстро выяснить, почему. Вы открываете JWT, а он выглядит как бессмысленный набор символов: три блока случайных знаков, разделённых точками. Данные там есть, но без парсера их не прочитать.
JWT-парсер — это специализированный инструмент, который разбивает три части JSON Web Token — Header, Payload и Signature — в соответствии со стандартом RFC 7519. По состоянию на апрель 2026 года такие парсеры декодируют данные в кодировке Base64URL и проверяют подписи с помощью секретов или открытых ключей, гарантируя, что токен не был изменён, и блокируя угрозы вроде атаки «alg: none».
Что на самом деле делает JWT-парсер
Представьте себе JWT-парсер как переводчика. Он берёт длинную непрозрачную строку и превращает её обратно в читаемые JSON-объекты. Это основа для управления пользовательскими идентификаторами и защиты обмена данными в современных приложениях.
Внутренне парсер находит две точки (.), которые делят токен на три секции:
| Секция | Назначение | Кодировка | Читаема без ключа? |
|---|---|---|---|
| Header | Метаданные: алгоритм подписи (HS256, RS256) | Base64URL | Да |
| Payload | Claims: данные пользователя, срок действия, роли | Base64URL | Да |
| Signature | Цифровая печать, подтверждающая подлинность | HMAC/RSA | Нет — требуется ключ |

Декодирование шаг за шагом: что происходит внутри
Давайте разберём реальный токен. Возьмём этот пример JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE3MDAwMDAwMDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Шаг 1: Разделение по точкам
[0] eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
[1] eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE3MDAwMDAwMDB9
[2] SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Шаг 2: Base64URL-декодирование секции [0] (Header)
{
"alg": "HS256",
"typ": "JWT"
}
Шаг 3: Base64URL-декодирование секции [1] (Payload)
{
"sub": "1234567890",
"name": "John",
"iat": 1700000000
}
Шаг 4: Проверка секции [2] (Signature) — требуется секретный ключ
Парсер берёт заголовок в кодировке Base64URL + «.» + payload, затем вычисляет HMAC-SHA256 с помощью секрета. Если результат совпадает с секцией [2], токен подлинный.
Важное замечание о безопасности: Base64URL — это не шифрование
Распространённая ловушка для начинающих разработчиков — предполагать, что закодированные заголовок и payload зашифрованы. Это не так. Как отмечает JustUse.me, кодировка Base64URL просто делает JSON безопасным для передачи через URL и заголовки. Любой, у кого есть токен, может декодировать payload без пароля или ключа.
Никогда не храните конфиденциальные данные (пароли, номера социального страхования, API-ключи) в payload JWT. Они видны любому, кто перехватит токен.
Проверка подписи: ворота безопасности
Хотя любой может прочитать данные токена, именно проверка подписи реально обеспечивает безопасность вашей системы. JWT-парсер не просто читает информацию — он доказывает, откуда она пришла.
Парсер заново вычисляет подпись, используя заголовок, payload и ключ, а затем проверяет, совпадает ли результат с подписью в токене. Если они не совпадают, значит, токен был изменён.
Два семейства алгоритмов
| Алгоритм | Тип ключа | Как это работает | Типичный сценарий использования |
|---|---|---|---|
| HS256 (HMAC) | Симметричный — один и тот же секретный ключ для подписи и проверки | Обе стороны используют один секрет | Аутентификация одного сервиса, микросервисы в одной команде |
| RS256 (RSA) | Асимметричный — закрытый ключ подписывает, открытый проверяет | Отправитель хранит закрытый ключ; любой с открытым ключом может проверить | Провайдеры OAuth2, сторонние интеграции с API |
| ES256 (ECDSA) | Асимметричный — та же модель, что у RSA, но на эллиптических кривых | Меньшие ключи, более быстрая проверка | Мобильные приложения, сервисы, чувствительные к производительности |

Атака «alg: none»
Это одна из самых опасных уязвимостей JWT. Злоумышленник изменяет заголовок, указывая "alg": "none", и удаляет подпись. Плохо реализованный парсер может принять такой токен как действительный без какой-либо проверки.
Защита: Ваш парсер должен явно отклонять любой токен, где алгоритм равен «none» или не соответствует ожидаемому алгоритму. Stas Persiianenko, разработчик инструмента Apify JWT, подчёркивает, что хотя токены по своей природе прозрачны, их безопасность зависит от того, насколько строго парсер отклоняет неподписанные или изменённые токены.
decoded = jwt.decode(token, key, algorithms=None) # НИКОГДА так не делайте
decoded = jwt.decode(token, key, algorithms=["HS256"])
Стандартные claims JWT: что означает каждое поле
JWT-парсер извлекает «claims» из payload. Они следуют фреймворку JOSE (JSON Object Signing and Encryption) для совместимости между системами.
| Claim | Полное название | Назначение | Пример значения |
|---|---|---|---|
iss |
Issuer | Кто выпустил токен | "auth.example.com" |
sub |
Subject | Пользователь или сущность, которую представляет токен | "user:12345" |
aud |
Audience | Предполагаемый получатель токена | "api.example.com" |
exp |
Expiration Time | Когда токен становится недействительным | 1700000000 (Unix timestamp) |
iat |
Issued At | Когда токен был создан | 1699999999 |
nbf |
Not Before | Токен недействителен до этого времени | 1699999999 |
jti |
JWT ID | Уникальный идентификатор токена | "a1b2c3d4" |
При использовании асимметричных подписей парсеры часто ссылаются на JWK (JSON Web Key) — JSON-структуру, представляющую открытый ключ. Парсер автоматически получает нужный JWK с metadata-эндпоинта эмитента для проверки токена.
Реализация: реальный код для продакшена
PHP с lcobucci/jwt
Стандартом в экосистеме PHP является lcobucci/jwt. По данным Packagist, на апрель 2026 года зафиксировано более 322 миллионов установок, что делает его выбором по умолчанию для проектов на Laravel и Symfony.
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
$config = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText('your-secret-key')
);
// Parsing and validating a token
$token = $config->parser()->parse($jwtString);
// Verify constraints: expiration, issuer, etc.
$constraints = [
new \Lcobucci\JWT\Validation\Constraint\IssuedBy('auth.example.com'),
new \Lcobucci\JWT\Validation\Constraint\PermittedFor('api.example.com'),
new \Lcobucci\JWT\Validation\Constraint\SignedWith(
$config->signer(),
$config->signingKey()
),
];
$isValid = $config->validator()->validate($token, ...$constraints);
Hono (Edge/Serverless) с Web Crypto
Для лёгких edge-приложений Hono JWT Helper предоставляет минимальную функцию decode(), идеально подходящую для бессерверных платформ, где важны быстрый холодный старт и минимум зависимостей.
import { jwt } from 'hono/jwt'
// Middleware to verify JWT on every request
app.use('/api/*', jwt({ secret: 'your-secret' }))
// Access decoded claims in your handler
app.get('/api/profile', (c) => {
const payload = c.get('jwtPayload')
return c.json({ user: payload.sub })
})
Анализ JWT с помощью ИИ и MCP
К 2026 году Model Context Protocol (MCP) позволяет ИИ-ассистентам вроде Claude Code или Cursor напрямую взаимодействовать с JWT-инструментами. Настройте MCP-сервер, и разработчик может попросить ИИ «Проверить все JWT в этих логах на ошибки истечения срока действия» — агент выполнит разбор через командную строку.
По данным Apify, по состоянию на 2026 год пакетная обработка стоит около $11.50 за 10,000 токенов. Такая автоматизация позволяет ИИ-агентам находить истекшие токены и сразу предлагать исправления кода для настроек безопасности приложения.
Заключение
JWT-парсер — это больше, чем просто удобство для отладки: это важнейшая контрольная точка безопасности. Он гарантирует подлинность токенов через проверку подписи и действительность — через проверку claims. Запомните два главных правила: Base64URL — это не шифрование, поэтому никогда не помещайте секреты в payload. И всегда явно указывайте разрешённые алгоритмы, чтобы предотвратить атаки «alg: none».
Для продакшен-приложений используйте проверенные библиотеки вроде lcobucci/jwt или JWT-хелпера Hono, а не пишите собственный парсер. Для отладки и массового анализа ИИ-инструменты на базе MCP — это современный подход к автоматизации и повышению thoroughness аудитов безопасности.
FAQ
Законно ли декодировать JWT-токен, найденный в моём браузере?
Да, это полностью законно. JWT созданы прозрачными — заголовок и payload закодированы для передачи, а не зашифрованы для секретности. Владение токеном подразумевает, что у вас есть доступ к данным в его claims. Однако всегда соблюдайте местные законы о защите данных, такие как GDPR, когда токены содержат персональную информацию.
Почему мой JWT-парсер показывает isExpired: true для токена, который я только что сгенерировал?
Обычно это вызвано рассинхронизацией часов между сервером, сгенерировавшим токен, и системой, которая его разбирает. Если часы двух систем не синхронизированы (через UTC/NTP), claims exp или nbf могут казаться недействительными. Исправьте это, обеспечив использование NTP для синхронизации времени на обеих системах, либо добавьте небольшой «leeway» (обычно 60 секунд) в вашей библиотеке разбора для учёта незначительных отклонений.
Могу ли я декодировать JWT без секрета или открытого ключа?
Да, вы всегда можете декодировать и прочитать Header и Payload без ключа, потому что это просто JSON в кодировке Base64URL. Однако вы не сможете проверить Signature или доверять подлинности данных без соответствующего секрета (для HS256) или открытого ключа (для RS256). Без проверки считайте данные непроверенными и потенциально изменёнными.
Что такое атака «alg: none» и как её предотвратить?
Атака «alg: none» эксплуатирует парсеры, которые принимают алгоритм, указанный в заголовке токена, без проверки. Злоумышленник меняет заголовок на "alg": "none" и удаляет подпись, заставляя уязвимый парсер принять токен как действительный. Предотвратите это, всегда явно указывая разрешённые алгоритмы в коде проверки — никогда не принимайте «none» и не позволяйте токену диктовать, какой алгоритм использовать.

Добавить комментарий