В 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);
Алгоритм работы:
passwords[0], пробуется расшифровкаpasswords[1]Это позволяет безопасно менять ключи без одновременного обновления всех зашифрованных данных.
Ротация реализуется через добавление нового ключа в начало массива:
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"]
Если один из старых ключей утёк, но продолжает оставаться в массиве:
Поэтому важно:
Типовой безопасный процесс:
При работе с массивом паролей важно учитывать:
Практика показывает:
const passwords = ['old', 'new']; // ошибка
В этом случае новые данные будут шифроваться старым ключом.
passwords.sort(); // критическая ошибка
Любая автоматическая сортировка ломает модель безопасности.
const passwords = ['key', 'key'];
Это полностью нивелирует смысл ротации.
В микросервисной архитектуре массив паролей позволяет:
Каждый сервис может временно иметь расширенный массив:
const passwords = [
'global-key-v4',
'global-key-v3',
'global-key-v2'
];
Часто используется подход двойной записи:
Это снижает риск потери данных при миграциях.
Каждая попытка расшифровки с несколькими ключами:
Некоторые реализации кешируют последний успешный пароль:
let lastGoodPassword = passwords[0];
и пробуют его первым.
При высокой нагрузке оптимизация выглядит так:
Это снижает среднюю стоимость операций.
Массив паролей в Iron — это не просто список секретов, а механизм: