Токен внутри токена

Механизм «токен внутри токена» в библиотеке Iron основан на идее многослойной защиты данных. Внешний токен выполняет роль контейнера, внутри которого находится другой токен — уже содержащий полезную нагрузку (payload) или дополнительные уровни аутентификации. Такой подход применяется для повышения безопасности, разделения ответственности и гибкости в управлении доступом.

В Iron каждый токен представляет собой зашифрованную и подписанную строку, формируемую на основе пароля (или набора паролей) и параметров криптографии. Вложенность означает, что результат одной операции seal становится входом для другой.


Архитектура вложенности

Структура выглядит следующим образом:

OuterToken = seal({
    inner: InnerToken,
    metadata: {...}
}, outerPassword)

InnerToken = seal({
    user: {...},
    permissions: [...]
}, innerPassword)

Ключевые особенности:

  • Изоляция слоёв — внутренний токен может быть расшифрован только при наличии соответствующего пароля.
  • Разные ключи — внешний и внутренний токены используют разные пароли/ключи.
  • Контроль доступа — можно передавать внешний токен, не раскрывая содержимое внутреннего.

Практическое применение

1. Делегирование доступа

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

  • Внешний токен — выдан API-шлюзом
  • Внутренний токен — содержит пользовательские данные

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

2. Многоуровневая аутентификация

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

Разделение позволяет обновлять или отзывать один уровень, не затрагивая другой.

3. Безопасная передача данных между сервисами

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


Реализация в Iron

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

const Iron = require('@hapi/iron');

const innerPayload = {
    userId: 123,
    role: 'admin'
};

const innerPassword = 'inner-secret';

const innerToken = await Iron.seal(innerPayload, innerPassword, Iron.defaults);

Создание внешнего токена

const outerPayload = {
    token: innerToken,
    issuedAt: Date.now()
};

const outerPassword = 'outer-secret';

const outerToken = await Iron.seal(outerPayload, outerPassword, Iron.defaults);

Распаковка вложенных токенов

Процесс происходит в обратном порядке:

1. Расшифровка внешнего токена

const outerData = await Iron.unseal(outerToken, outerPassword, Iron.defaults);

2. Извлечение и расшифровка внутреннего токена

const innerData = await Iron.unseal(outerData.token, innerPassword, Iron.defaults);

Важные нюансы

Разделение ключей

Использование разных паролей критически важно:

  • снижает риск компрометации
  • позволяет управлять уровнями независимо

Срок жизни токенов

Можно задавать разные TTL:

  • внешний токен — короткий срок (например, сессия)
  • внутренний токен — более длительный

Проверка целостности

Каждый слой:

  • подписан (HMAC)
  • зашифрован (AES)

Это означает, что:

  • изменение внутреннего токена ломает внешний
  • изменение внешнего не влияет на внутренний, но делает его недоступным

Ошибки и уязвимости

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

Использование одного и того же пароля для обоих уровней:

  • нивелирует преимущества вложенности
  • упрощает атаки

Избыточная вложенность

Слишком глубокая структура:

  • усложняет отладку
  • увеличивает накладные расходы

Оптимально — 2 уровня.

Утечка внутреннего токена

Если внутренний токен передаётся отдельно:

  • он должен считаться самостоятельным секретом
  • его компрометация не должна раскрывать внешний уровень

Оптимизация производительности

Каждый вызов seal и unseal:

  • использует PBKDF2 (или аналог)
  • требует вычислительных ресурсов

Рекомендации:

  • кэшировать расшифрованные данные при необходимости
  • не выполнять вложенные операции в горячих участках кода
  • использовать асинхронные вызовы

Расширенные сценарии

Вложенные токены с разными алгоритмами

Iron позволяет настраивать параметры:

const options = {
    encryption: {
        algorithm: 'aes-256-cbc'
    },
    integrity: {
        algorithm: 'sha256'
    }
};

Внутренний и внешний токены могут использовать разные конфигурации.


Передача контекста

Внешний токен может содержать:

  • метаданные запроса
  • информацию о клиенте
  • trace-id для логирования

Внутренний — только бизнес-данные.


Ротация ключей

Можно обновлять:

  • внешний ключ без изменения внутреннего токена
  • внутренний ключ при сохранении внешней оболочки

Это особенно полезно в распределённых системах.


Сравнение с альтернативами

JWT

JWT не поддерживает нативную вложенность:

  • требует ручного шифрования
  • сложнее в управлении

Iron:

  • автоматически шифрует
  • обеспечивает целостность
  • проще в реализации вложенных структур

Типичный шаблон использования

  1. Генерация внутреннего токена (данные пользователя)

  2. Оборачивание во внешний токен (сессионный уровень)

  3. Передача внешнего токена клиенту

  4. По запросу:

    • проверка внешнего
    • извлечение внутреннего
    • работа с данными

Диагностика и отладка

Для анализа:

  • логировать этапы seal/unseal
  • использовать разные пароли для теста
  • проверять TTL и параметры

Ошибки чаще всего возникают из-за:

  • неверного пароля
  • повреждённой строки токена
  • несовпадения настроек

Безопасностные рекомендации

  • хранить пароли в защищённых хранилищах (env, vault)
  • не логировать токены целиком
  • использовать разные ключи для разных окружений
  • ограничивать срок жизни внешних токенов

Итоговая модель

Вложенные токены в Iron — это:

  • механизм многоуровневой защиты
  • инструмент разделения ответственности
  • способ безопасной передачи данных между системами

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