Если мне будет не лень, то все статьи со скриншота окажутся опубликованы.

План серии статей о JWT

Содержание:

JSON Web Token - это формат токенов для аутентификации и авторизации в вебе. Компактная строка из трех частей, разделенных точками, которую сервер выдает клиенту после логина, а клиент отправляет обратно с каждым запросом. Сервер проверяет подпись, читает содержимое - и знает, кто перед ним.

Звучит просто. На практике - это самый взламываемый стандарт в веб-безопасности.

В 2015 году Тим Маклин нашел способ подделать токен любого пользователя в любом сервисе, использующем JWT. Одна подмена поля в заголовке - и сервер принимает поддельный токен как настоящий. Одиннадцать лет спустя, в 2026-м, тот же самый баг нашли в Hono framework (CVE-2026-22817, CVSS 8.2). Один и тот же баг, одиннадцать лет.

Давай разберемся, почему JWT упорно остается дырявым.

Зачем вообще придумали JWT

До JWT веб-приложения работали на серверных сессиях. Пользователь логинится, сервер создает сессию, кладет ее в Redis или в память, возвращает cookie с session ID. При каждом запросе сервер ищет сессию по ID и проверяет, кто перед ним.

Это работало, пока приложения жили на одном сервере. Но когда пришла эпоха микросервисов, стало больно. Десять микросервисов, каждый должен знать, кто делает запрос. Гонять их всех к одному Redis за сессией? Или дать каждому копию? Оба варианта масштабируются плохо.

JWT решает эту проблему. Токен содержит всю информацию внутри себя: кто пользователь, какие у него права, когда токен истекает. Сервер подписывает эти данные криптографическим ключом. Любой микросервис может проверить подпись и прочитать токен - без похода в базу, без общего хранилища. Это называется stateless-аутентификация.

Красивая идея. Но в ней заложены проблемы, о которых мы будем говорить всю серию.

JWT - это не один стандарт

Когда говорят “JWT”, подразумевают один формат. На деле за ним стопка из шести RFC под названием JOSE (JSON Object Signing and Encryption): JWT определяет формат токена и claims, JWS описывает подписи, JWE - шифрование, JWK - формат ключей, JWA - алгоритмы, а RFC 8725 - рекомендации по безопасности. Шесть спецификаций, сотни страниц. Сложность порождает баги.

Фундаментальный дефект дизайна

Вот ключевая проблема, к которой я буду возвращаться на протяжении всей серии.

Токен сам указывает серверу, как его проверять. В заголовке JWT есть поле alg - “проверь мою подпись алгоритмом X”. Это как если бы ключ говорил замку “открывайся без проверки”.

Атакующий меняет alg на none - сервер пропускает верификацию. Меняет RS256 на HS256 - и публичный ключ превращается в пароль. Я подробно покажу, как это работает, в частях 3 и 4. Пока запомни главное: доверие алгоритму из заголовка токена - архитектурная ошибка, и именно она порождает большинство уязвимостей JWT.

Статистика

70+ CVE за десять лет. Если разбить их по категориям, картина печальная. Algorithm confusion и alg:none - классика, которую находят снова и снова. Но вот что удивительно: 17% свежих CVE за 2024-2026 - это даже не криптографические баги. Это тупо захардкоженные секреты.

Cisco IOS XE (CVE-2025-20188, CVSS 10.0) подписывал JWT строкой notfound. Буквально слово “notfound” как секрет подписи. Dpanel (CVE-2025-30206, CVSS 9.8) - секрет прямо в исходном коде на GitHub. Десятибалльная уязвимость из-за того, что разработчик не осилил сгенерировать случайную строку.

Три вещи, которые делают JWT лакомым куском

Токен самодостаточен. В серверных сессиях при компрометации можно удалить сессию из Redis - и все, доступ закрыт. С JWT так не работает. Нет серверного хранилища - нет мгновенного отзыва. Украл токен - пользуешься до истечения exp. Logout на стороне клиента не поможет, потому что сервер не знает, что токен украден.

Base64 - это не шифрование. Payload JWT закодирован, а не зашифрован. Любой может прочитать содержимое без единого ключа:

echo "eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJzdXBlcnVzZXIifQ" | base64 -d

Результат: {"sub":"admin","role":"superuser"}. Роли, имена, email-адреса, идентификаторы - в открытом виде. Подпись гарантирует целостность (никто не подменил данные), но не конфиденциальность (кто угодно может их прочитать). Существует JWE - зашифрованный вариант JWT, но на практике 99% токенов в дикой природе - это JWS, то есть подписанные, но не зашифрованные.

Секреты можно брутить офлайн. JWT содержит все необходимое для офлайн-атаки: сообщение (заголовок и payload) и подпись. Один перехваченный токен - и hashcat на GPU перебирает 150 миллионов паролей в секунду. Секрет “password” ломается мгновенно. Тебе не нужен доступ к серверу. Тебе нужен один токен.

Почему это не починят

JWT намертво встроен в OAuth 2.0, OpenID Connect и практически каждый API. Альтернативы существуют: PASETO, например, убирает поле alg из токена и тем самым устраняет целый класс атак. Но экосистема PASETO в тысячу раз меньше. Библиотеки JWT есть для каждого языка, документация повсюду, Stack Overflow завален примерами. Переход на альтернативу - это переписывание инфраструктуры.

Интересный факт: Google для браузерных сессий использует обычные cookies и серверные сессии, а не JWT. Если крупнейшая компания планеты решила, что JWT для сессий - плохая идея, может стоит прислушаться.

Но JWT никуда не денется. Миллионы приложений используют его прямо сейчас, и каждый день появляются новые. А значит - нужно понимать, как его ломать. И как защищать.

Что дальше

Эта серия - практическое руководство по атакам на JWT. Мы разберем токен до последнего байта, разложим каждый вектор атаки с конкретными PoC, и к концу серии у тебя будет полная методология пентеста JWT.

В следующей статье берем реальный токен и разбираем как патологоанатом: header, payload, signature, Base64url, claims. После нее ты будешь читать токены глазами.