Что такое JWT: компактный носитель утверждений

JSON Web Token (JWT) — это открытый промышленный стандарт RFC 7519 для компактной и автономной передачи данных между сторонами в виде JSON-объекта. Главное назначение JWT — подтверждение подлинности (аутентификация) и прав доступа (авторизация) пользователя в распределенных микросервисах и REST API.

Ключевое свойство классического JWT: он автономен (stateless). Серверу не требуется обращаться к общей базе данных сессий при каждом входящем HTTP-запросе. Вся необходимая контекстная информация (ID пользователя, роль, права и срок годности) уже «зашита» в тело токена, а математическая подпись гарантирует, что данные не были подделаны по пути.

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

Анатомия JWT: три части через точку

Любой корректный JWT токен представляет собой строку, состоящую из трех частей, разделенных точками: header.payload.signature

1. Header (Заголовок)

Определяет метаданные токена: тип объекта (всегда "JWT") и алгоритм вычисления подписи. Например, симметричный HS256 (HMAC-SHA256) или асимметричный RS256 (RSA с публичным и приватным ключами).

{
  "alg": "HS256",
  "typ": "JWT"
}
2. Payload (Полезная нагрузка / Claims)

Содержит сами утверждения (claims) о пользователе и контексте сессии. Стандарт выделяет зарегистрированные (стандартные) и произвольные пользовательские поля.

{
  "sub": "user_98412",
  "name": "Alex Miller",
  "role": "editor",
  "iat": 1792929600,
  "exp": 1792933200
}
3. Signature (Цифровая подпись)

Математический хэш от склеенных заголовка и тела, создаваемый с секретным ключом. Вычисляется по формуле:

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 декодер→

Частые вопросы

Зашифрован ли 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) в профиле пользователя в базе данных.