alltools.one
Security
2025-06-12
8 min
alltools.one Team
JWTSecurityAuthenticationAPIWeb Security

Безопасность JSON Web Token: распространённые уязвимости и их устранение

JWT являются стандартом де-факто для аутентификации API, но за их кажущейся простотой скрываются реальные проблемы безопасности. Неправильно настроенные JWT приводили к обходу аутентификации, повышению привилегий и утечкам данных. В этом руководстве рассматриваются наиболее критичные уязвимости и способы их предотвращения.

Краткий обзор структуры JWT

JWT состоит из трёх частей, закодированных в Base64URL и разделённых точками:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.signature
  • Заголовок (Header): алгоритм и тип токена
  • Полезная нагрузка (Payload): утверждения (данные пользователя, срок действия и т.д.)
  • Подпись (Signature): криптографическая проверка

Изучите структуру JWT с помощью нашего JWT-кодировщика/декодировщика.

Для фундаментального понимания структуры JWT смотрите наше руководство JWT-токены: объяснение.

Критические уязвимости

1. Атака подмены алгоритма

Самая опасная уязвимость JWT. Если сервер принимает заголовок alg из токена без проверки, злоумышленник может:

Атака: изменить алгоритм с RS256 (асимметричный) на HS256 (симметричный) и подписать поддельный токен публичным ключом сервера:

// Attacker's forged token
header: { "alg": "HS256", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
// Signed with the server's PUBLIC key as the HMAC secret

Если сервер проверяет HS256-токены, используя публичный ключ в качестве секрета, поддельный токен проходит верификацию.

Исправление: всегда явно указывайте ожидаемый алгоритм:

// WRONG - accepts whatever algorithm the token specifies
jwt.verify(token, key);

// CORRECT - enforce specific algorithm
jwt.verify(token, key, { algorithms: ['RS256'] });

2. Атака с алгоритмом none

Некоторые библиотеки принимают "alg": "none" — токен без подписи:

// Forged token with no signature
header: { "alg": "none", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
signature: ""  // empty

Исправление: никогда не допускайте алгоритм none в продакшене. Явно задавайте список разрешённых алгоритмов.

3. Слабые секреты подписи

JWT на основе HMAC (HS256/HS384/HS512) надёжны ровно настолько, насколько надёжен секрет:

// TERRIBLE - can be brute-forced in seconds
secret = "password123"

// WEAK - dictionary attack vulnerable
secret = "my-jwt-secret"

// STRONG - 256+ bits of randomness
secret = "a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"

Исправление: используйте минимум 256 бит криптографической случайности для HMAC-секретов. Ещё лучше — применяйте асимметричные ключи (RS256, ES256), где ключ подписи не нужно разделять.

4. Отсутствие срока действия

Токены без срока действия никогда не истекают — украденный токен предоставляет постоянный доступ:

// WRONG - no expiration
{ "sub": "user123", "role": "admin" }

// CORRECT - short expiration
{
  "sub": "user123",
  "role": "admin",
  "exp": 1705312800,
  "iat": 1705309200,
  "nbf": 1705309200
}

Лучшая практика: токены доступа истекают через 15-60 минут. Используйте refresh-токены (хранящиеся безопасно) для длительных сессий.

5. Конфиденциальные данные в полезной нагрузке

Полезная нагрузка JWT закодирована в Base64URL, но не зашифрована. Любой может её декодировать:

echo "eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ" | base64 -d
# {"sub":"123","role":"admin"}

Никогда не включайте: пароли, API-ключи, номера кредитных карт, персональные данные (номер паспорта, медицинские записи) или любые секреты в полезную нагрузку JWT.

6. Хранение токенов (XSS vs CSRF)

ХранилищеУязвимо к XSSУязвимо к CSRF
localStorageДаНет
Cookie (без флагов)ДаДа
HttpOnly CookieНетДа
HttpOnly + SameSite CookieНетНет

Рекомендация: храните JWT в HttpOnly, Secure, SameSite=Strict cookie. Это предотвращает доступ через JavaScript (защита от XSS) и межсайтовые запросы (защита от CSRF).

Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600

Чеклист безопасности

  1. Явно указывайте алгоритмы — никогда не принимайте из заголовка токена
  2. Отклоняйте алгоритм none — всегда требуйте подпись
  3. Используйте надёжные секреты — 256+ бит для HMAC, 2048+ бит для RSA
  4. Устанавливайте короткий срок действия — 15-60 минут для токенов доступа
  5. Валидируйте все утвержденияexp, iss, aud, nbf
  6. Храните в HttpOnly cookie — не в localStorage
  7. Периодически ротируйте секреты — планируйте ротацию ключей
  8. Никогда не храните конфиденциальные данные в полезной нагрузке
  9. Используйте асимметричные ключи для распределённых систем (RS256 или ES256)
  10. Реализуйте отзыв токенов — чёрный список или короткий срок действия + refresh-токены

Асимметричная vs симметричная подпись

АспектHS256 (Симметричная)RS256 (Асимметричная)
КлючОбщий секретПара приватный/публичный ключ
Кто может подписыватьЛюбой, у кого есть секретТолько владелец приватного ключа
Кто может проверятьЛюбой, у кого есть секретЛюбой, у кого есть публичный ключ
Распространение ключейСекрет нужно безопасно передаватьПередаётся только публичный ключ
ПроизводительностьБыстрееМедленнее
Лучше подходит дляЕдиного сервисаМикросервисов, распределённых систем

Рекомендация: используйте ES256 (ECDSA) для новых приложений — он обеспечивает асимметричную безопасность с производительностью, близкой к HMAC.

Стратегии отзыва токенов

JWT являются stateless по своей сути — встроенного способа их отозвать нет. Стратегии:

  1. Короткий срок действия: если токены истекают через 15 минут, окно использования украденного токена ограничено
  2. Ротация refresh-токенов: выпуск нового refresh-токена при каждом использовании; если refresh-токен использован дважды — отзыв всех токенов
  3. Чёрный список: хранение ID отозванных токенов (утверждения jti) и проверка при каждом запросе
  4. Версионирование токенов: включение номера версии в утверждения; инкремент версии пользователя при выходе из системы

FAQ

Что лучше использовать для аутентификации: JWT или сессионные cookie?

Сессионные cookie проще и безопаснее для традиционных веб-приложений — сервер контролирует жизненный цикл сессии, и отзыв происходит мгновенно. JWT лучше подходят для stateless API, микросервисов и мобильных приложений, где серверное хранение сессий непрактично. Если вы выбираете JWT, реализуйте меры безопасности из этого руководства.

Какой идеальный срок действия JWT?

Для токенов доступа: 15-60 минут. Меньший срок безопаснее, но требует более частого обновления. Для refresh-токенов: 1-30 дней, хранение в HttpOnly cookie. Для одноразовых токенов (подтверждение email, сброс пароля): 1-24 часа.

Связанные ресурсы

Published on 2025-06-12
JSON Web Token Security: Common Vulnerabilities and Fixes | alltools.one