Понимание атак и уязвимостей JSON Web Token (JWT)
JSON Web Token (JWT) стали фундаментальной технологией для безопасной передачи информации между сторонами в формате JSON. Благодаря возможности цифровой подписи и верификации, JWT широко используются для авторизации и аутентификации в современных веб-приложениях. Однако безопасность JWT зависит от точной реализации и настройки. Неправильное использование может привести к серьёзным уязвимостям и рискам эксплуатации.
Строение JSON Web Token
В основе JWT лежат три компонента — заголовок, полезная нагрузка и подпись — каждый из которых закодирован в Base64URL и разделён точками, имея следующую структуру:
HEADER.PAYLOAD.SIGNATURE
Заголовок JWT
Заголовок определяет метаданные токена, включая:
- alg: криптографический алгоритм для подписи токена (например, HS256, RS256)
- typ: тип токена, обычно “JWT”
Пример декодированного заголовка:
{
"alg": "HS256",
"typ": "JWT"
}
Полезная нагрузка JWT
Полезная нагрузка содержит утверждения (claims), которые представляют собой данные об объекте (обычно пользователе) и дополнительную информацию. Например:
{
"name": "John Doe",
"user_name": "john.doe",
"is_admin": false
}
Утверждения могут быть стандартными (зарегистрированными, например, iss, exp) или настраиваемыми под конкретную логику приложения.
Подпись JWT
Подпись обеспечивает целостность и подлинность токена. Она создаётся путём подписания Base64URL-кодированных заголовка и полезной нагрузки с использованием секрета или приватного ключа, в зависимости от алгоритма:
- Закодировать заголовок и полезную нагрузку
- Объединить их точкой
- Подписать полученную строку указанным алгоритмом (например, HMAC с SHA-256 или RSA)
Например, для алгоритма HS256:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Распространённые уязвимости в реализации JWT
Несмотря на широкое распространение, JWT подвержены ряду уязвимостей, возникающих из-за ошибок конфигурации или реализации. Эти слабые места могут поставить под угрозу безопасность приложений, использующих JWT для аутентификации и авторизации.
1. Пропуск проверки подписи
Многие библиотеки JWT различают декодирование и проверку токена. Функция decode() извлекает содержимое без проверки подписи, тогда как verify() гарантирует правильность подписи. Разработчики, случайно полагающиеся только на decode(), рискуют принять поддельные токены.
Последствия: Злоумышленники могут произвольно изменять токены — повышать права или выдавать себя за других пользователей без обнаружения. Например, изменение утверждения is_admin с false на true в неподписанном токене может предоставить несанкционированный доступ администратора.
2. Разрешение использования алгоритма “None”
Спецификация JWT предусматривает опцию “None” — отсутствие подписи. Если приложение допускает токены с alg: none, злоумышленники могут обойти проверку подписи, полностью удалив её.
Пример из практики: Исторически были успешные атаки, использовавшие принятие alg: none для компрометации сервисов (см. анализ Auth0).
Меры защиты: Явно запрещать алгоритм none и выполнять проверку допустимых алгоритмов по белому списку.
3. Атаки путаницы алгоритмов
JWT поддерживает симметричные (HMAC) и асимметричные (RSA, ECDSA) алгоритмы. Проблемы возникают, когда приложение не проверяет тип алгоритма и позволяет злоумышленнику заменить его (например, RSA на HMAC), обманув проверяющий модуль для использования неподходящего ключа.
Пример: Злоумышленник заменяет alg: RS256 на alg: HS256 и использует публичный ключ как секрет для HMAC, что позволяет подделывать подписи без приватного ключа.
Эта уязвимость отмечена в нескольких библиотеках JWT и остаётся критичной при отсутствии строгой проверки алгоритма.
4. Использование слабых или легко угадываемых секретов для симметричной подписи
В симметричной криптографии (например, HS256) безопасность токена полностью зависит от секретности общего ключа. Слабые секреты, такие как простые пароли или предсказуемые строки, уязвимы к переборам.
Результаты исследований: Анализы показывают, что многие JWT используют слабые секреты, например “123456” или “password” (см. отчёт Snyk по уязвимостям JWT 2023), что облегчает злоумышленникам компрометацию аутентификации.
Рекомендации: Используйте длинные, случайно сгенерированные секреты — желательно более 256 бит — и регулярно обновляйте их.
5. Эксплуатация параметра Key ID (kid)
Параметр kid в заголовке JWT служит для выбора ключа проверки подписи. При отсутствии валидации его можно эксплуатировать разными способами:
- Инъекции: Зловредный ввод в
kidможет привести к SQL-инъекциям, удалённому выполнению кода (RCE) или локальному включению файлов (LFI), если применение небезопасно для получения ключей. - Обход путём перехода по каталогам: Злоумышленники меняют
kid, чтобы ссылаться на произвольные файлы (например,/dev/null) для обхода проверки подписи. - SQL-инъекция: Несанкционированные SQL-запросы с пользовательским
kidпозволяют подменить ключ и подделать подпись.
Совет по безопасности: Очищайте и строго валидируйте параметр kid перед использованием. Применяйте подготовленные выражения для запросов к БД и ограничивайте доступ к файловой системе.
6. Злоупотребление параметром jku
Параметр jku (URL набора JSON Web Key Set) позволяет динамически загружать публичные ключи с удалённого URL. Без должной проверки злоумышленники могут направить приложение на свой JWKS endpoint с поддельными ключами.
- Атакующий вектор: Изменить
jkuна злоумышленнический URL, сгенерировать пару ключей и подписывать токены, которые приложение ошибочно примет. - Обход защиты: Использование хитростей URL, открытых редиректов, DNS-уловок и SSRF для обхода доменных ограничений или фильтров URL.
Меры снижения риска:
- Белый список доверенных доменов для значений
jkuи строгая проверка формата URL. - Использование статических или внутренних ключей вместо динамического получения.
- Реализация надёжных мер защиты от SSRF для предотвращения несанкционированных запросов.
Дополнительные рекомендации и лучшие практики
- Используйте проверенные библиотеки JWT: Реализуйте JWT с помощью хорошо поддерживаемых библиотек с регулярными обновлениями безопасности.
- Обеспечьте срок действия токенов: Устанавливайте разумные времена жизни (
exp) и строго проверяйте их, чтобы ограничить повторное использование. - Проверяйте аудиторию и издателя: Подтверждайте, что токен предназначен для вашего приложения, проверяя утверждения
audиiss. - Регулярно обновляйте секреты и ключи: Периодически меняйте криптографические ключи и секреты для минимизации последствий утечек.
- Применяйте многоуровневую защиту: Верификация JWT должна быть частью комплексной стратегии безопасности с серверными проверками авторизации, ограничением частоты запросов и обнаружением аномалий.
Реальные примеры
Пример 1: Уязвимость путаницы алгоритмов в Auth0
В 2015 году Auth0 раскрыли критическую уязвимость JWT, когда злоумышленник мог обойти аутентификацию, используя путаницу алгоритмов, что подчеркнуло важность проверки алгоритма (источник).
Пример 2: Ошибка настройки JWT в Microsoft Azure Container Service
В 2018 году неправильная валидация JWT в Azure Container Service позволила повысить привилегии, допуская токены с алгоритмом none. Уязвимость была исправлена после сообщения (источник).
Заключение
JSON Web Token являются важным инструментом современной аутентификации и авторизации, но несут значительные риски при неправильной реализации. Злоумышленники используют уязвимости, такие как пропуск проверки подписи, путаница алгоритмов, слабые секреты и небезопасная обработка параметров заголовка JWT, таких как kid и jku. Чтобы защитить приложения, разработчикам необходимо строго следовать лучшим практикам, тщательно валидировать все элементы JWT и использовать надёжные библиотеки.
Регулярные оценки уязвимостей и тестирование безопасности, ориентированное на JWT, жизненно необходимы для поддержания высокой безопасности приложений. Устранение уязвимостей JWT снижает риски несанкционированного доступа, повышения привилегий и масштабных нарушений безопасности.

