Вложенные JWT

Вложенные JWT (Nested JWT) представляют собой композицию токенов, где один JWT инкапсулируется в другой уровень защиты. На практике это чаще всего означает, что подписанный токен (JWS) дополнительно шифруется (JWE), формируя структуру вида «подписать, затем зашифровать». Такой подход позволяет одновременно обеспечить целостность данных и их конфиденциальность при передаче между системами.

Базовая модель JWT опирается на три формата, определённых спецификацией JOSE:

  • JWS (JSON Web Signature) — подписанный токен
  • JWE (JSON Web Encryption) — зашифрованный токен
  • JWT — общий термин, который может означать как JWS, так и JWE

Вложенный JWT строится как комбинация:

JWS → JWE

Сначала создаётся подписанный токен, затем он используется как полезная нагрузка для шифрования.

Итоговая структура:

  • Внутренний уровень: JSON payload + подпись (JWS)
  • Внешний уровень: шифрование результата (JWE)

Таким образом, содержимое защищено дважды:

  • подпись гарантирует неизменность
  • шифрование скрывает данные

Подходы к вложенности

Существуют два основных паттерна:

Подписание перед шифрованием (Sign-then-Encrypt)

Наиболее распространённый вариант:

  1. Формируется JWT payload
  2. Создаётся JWS (подпись)
  3. Полученный JWS шифруется в JWE

Этот подход обеспечивает:

  • невозможность подмены данных
  • скрытие содержимого токена

Шифрование перед подписанием (Encrypt-then-Sign)

Редко используемая схема:

  1. Создаётся JWE
  2. Поверх него накладывается подпись JWS

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

Библиотека Jose и работа с вложенными JWT

Библиотека jose предоставляет низкоуровневые и высокоуровневые инструменты для работы с JWS и JWE в Node.js и браузере. Основные используемые компоненты:

  • SignJWT — создание подписанного JWT
  • jwtVerify — проверка подписи
  • CompactEncrypt — создание JWE
  • compactDecrypt — расшифровка JWE

Комбинирование этих примитивов позволяет вручную строить вложенные токены.

Формирование JWS (подписанный JWT)

Сначала создаётся подписанный токен:

import { SignJWT } from 'jose'

const secretKey = new TextEncoder().encode('access-secret-key')

const jws = await new SignJWT({ userId: 123, role: 'admin' })
  .setProtectedHeader({ alg: 'HS256' })
  .setIssuedAt()
  .setExpirationTime('2h')
  .sign(secretKey)

Результатом является компактный JWS в формате:

header.payload.signature

Шифрование JWS в JWE

Далее подписанный токен используется как payload для шифрования:

import { CompactEncrypt } from 'jose'

const encryptionKey = new TextEncoder().encode('encryption-secret-key')

const jwe = await new CompactEncrypt(
  new TextEncoder().encode(jws)
)
  .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
  .encrypt(encryptionKey)

На этом этапе получается полностью вложенный JWT:

  • внутри: JWS (подписанные данные)
  • снаружи: JWE (шифрованная оболочка)

Расшифровка и проверка вложенного JWT

Обратный процесс требует двух шагов: расшифровать и затем проверить подпись.

Шаг 1: Расшифровка JWE

import { compactDecrypt } from 'jose'

const { plaintext } = await compactDecrypt(jwe, encryptionKey)

const innerJws = new TextDecoder().decode(plaintext)

После этого извлекается исходный JWS.

Шаг 2: Проверка подписи JWS

import { jwtVerify } from 'jose'

const { payload } = await jwtVerify(innerJws, secretKey)

console.log(payload)

Архитектурные особенности вложенных JWT

Вложенные токены применяются в системах, где требуется разделение ответственности:

  • внешний уровень отвечает за конфиденциальность
  • внутренний уровень отвечает за подлинность данных

Это особенно важно в распределённых системах, где токен проходит через несколько сервисов, но не должен быть читаемым на промежуточных этапах.

Практические сценарии использования

Межсервисная коммуникация

Сервис A формирует токен, сервис B расшифровывает и проверяет подпись, сервис C уже не имеет доступа к содержимому без ключа.

Финансовые системы

Часто используется для защиты транзакционных данных, где утечка payload недопустима даже при перехвате токена.

Identity Federation

В сценариях SSO вложенные JWT позволяют разделить:

  • внешний уровень (шифрование между доменами)
  • внутренний уровень (подпись провайдера идентификации)

Управление ключами

Критическим аспектом является разделение ключей:

  • ключ подписи (HMAC / RSA / EC)
  • ключ шифрования (AES / RSA-OAEP)

Рекомендуется:

  • использовать разные ключи для JWS и JWE
  • хранить ключи в отдельных системах управления секретами
  • регулярно ротировать ключи
  • избегать использования симметричных ключей для обоих уровней

Типичные ошибки при реализации

Использование одного ключа для подписи и шифрования

Это снижает уровень безопасности и нарушает принцип разделения обязанностей.

Проверка только внешнего уровня

Расшифровка JWE без проверки JWS делает систему уязвимой к подмене данных внутри payload.

Неправильный порядок операций

Шифрование до подписи часто приводит к невозможности верификации промежуточными сервисами и усложняет архитектуру.

Потеря контекста алгоритмов

Каждый уровень должен явно задавать алгоритмы:

  • JWS: HS256, RS256, ES256
  • JWE: A256GCM, RSA-OAEP, dir

Несогласованность алгоритмов приводит к ошибкам декодирования.

Композиция вложенных JWT в сложных системах

В системах с высокой степенью безопасности возможно многоуровневое вложение:

  • JWS (подпись сервиса)
  • JWE (шифрование канала)
  • повторное JWE на уровне транспортного шлюза

Хотя технически возможно построение цепочек, каждый дополнительный слой увеличивает:

  • задержку обработки
  • сложность отладки
  • требования к управлению ключами

Поэтому архитектура должна оставаться минимально достаточной.

Особенности реализации в Jose

Библиотека jose не предоставляет отдельного «nested JWT builder», так как концепция вложенности строится композиционно:

  • сначала создаётся JWS через SignJWT
  • затем применяется CompactEncrypt
  • обратные операции выполняются вручную через compactDecrypt и jwtVerify

Такой подход даёт гибкость и контроль над каждым уровнем криптографической защиты.