Массив паролей для ротации

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

В основе Iron лежит симметричное шифрование с использованием общего секрета — password. Однако в реальных приложениях один и тот же секрет нельзя использовать бесконечно. Причины:

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

Поэтому вместо одного значения допускается использование массива паролей:

const passwords = [
  'current-secret-password',
  'previous-secret-password',
  'legacy-secret-password'
];

Первый элемент массива считается основным и используется для шифрования новых данных. Остальные элементы применяются только для расшифровки.

Логика при шифровании

При создании защищённой структуры Iron всегда использует первый пароль массива:

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

const passwords = ['new-secret', 'old-secret'];

const sealed = await Iron.seal(payload, passwords[0], Iron.defaults);

Фактически библиотека сама ожидает, что в конфигурации будет передан массив, и самостоятельно выбирает актуальный ключ, если используется стандартный API:

const sealed = await Iron.seal(payload, passwords, Iron.defaults);

В этом случае:

  • passwords[0] → шифрование
  • остальные → резервные значения

Логика при расшифровке

При расшифровке Iron перебирает весь массив паролей последовательно до первого успешного совпадения:

const unsealed = await Iron.unseal(sealed, passwords, Iron.defaults);

Алгоритм работы:

  1. Берётся passwords[0], пробуется расшифровка
  2. Если ошибка — берётся passwords[1]
  3. Процесс продолжается до конца массива
  4. Если ни один пароль не подошёл — выбрасывается ошибка

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

Стратегия ротации паролей

Ротация реализуется через добавление нового ключа в начало массива:

Старая версия

const passwords = [
  'key-v2',
  'key-v1'
];

После обновления

const passwords = [
  'key-v3',
  'key-v2',
  'key-v1'
];

Новые данные шифруются с key-v3, но старые продолжают корректно расшифровываться.

Удаление устаревших ключей

Удаление старых паролей возможно только после полной миграции данных.

Если удалить ключ, используемый для расшифровки старых объектов, они станут недоступны:

const passwords = [
  'key-v3',
  'key-v2' // key-v1 удалён
];

Теперь все данные, зашифрованные key-v1, перестанут декодироваться.

Практическая модель хранения конфигурации

В реальных приложениях массив паролей обычно хранится в конфигурации окружения:

const passwords = process.env.IRON_PASSWORDS.split(',');

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

const passwords = JSON.parse(process.env.IRON_PASSWORDS);

Пример .env:

IRON_PASSWORDS=["key-v3","key-v2","key-v1"]

Поведение при частичной компрометации

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

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

Поэтому важно:

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

Рекомендованный порядок ротации

Типовой безопасный процесс:

  1. Добавление нового ключа в начало массива
  2. Переход на новый ключ для всех операций записи
  3. Постепенная миграция данных (если применимо)
  4. Удаление самого старого ключа после завершения миграции

Особенности поведения Iron

При работе с массивом паролей важно учитывать:

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

Оптимальная длина массива

Практика показывает:

  • 2–3 ключа — оптимально
  • 5+ ключей — уже заметное замедление
  • 10+ ключей — признак проблемной стратегии ротации

Типичные ошибки при использовании

Потеря порядка

const passwords = ['old', 'new']; // ошибка

В этом случае новые данные будут шифроваться старым ключом.

Случайная сортировка

passwords.sort(); // критическая ошибка

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

Использование одинаковых ключей

const passwords = ['key', 'key'];

Это полностью нивелирует смысл ротации.

Использование в распределённых системах

В микросервисной архитектуре массив паролей позволяет:

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

Каждый сервис может временно иметь расширенный массив:

const passwords = [
  'global-key-v4',
  'global-key-v3',
  'global-key-v2'
];

Безопасное обновление через фичу переключения

Часто используется подход двойной записи:

  • новый ключ сразу в production
  • старый ключ остаётся только для чтения
  • через время старые ключи удаляются

Это снижает риск потери данных при миграциях.

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

Каждая попытка расшифровки с несколькими ключами:

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

Некоторые реализации кешируют последний успешный пароль:

let lastGoodPassword = passwords[0];

и пробуют его первым.

Кэширование успешного ключа

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

  1. Сначала пробуется последний успешный пароль
  2. Затем основной массив
  3. При успехе обновляется cache

Это снижает среднюю стоимость операций.

Резюме модели работы массива

Массив паролей в Iron — это не просто список секретов, а механизм:

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