В распределённых JavaScript-системах состояние часто становится источником сложности: необходимость синхронизации, обращения к общей базе данных, управление сессиями и согласованность данных между сервисами резко увеличивают связность архитектуры. Один из способов уйти от централизованного хранения контекста — переносить его в защищённом виде вместе с запросом, используя криптографически «запечатанные» структуры данных.
Библиотека @hapi/iron решает задачу безопасного
упаковывания произвольного JavaScript-объекта в строку, которую можно
передавать между сервисами или хранить на клиенте без риска изменения
содержимого. Такой подход позволяет реализовать передачу контекста без
базы данных, сохраняя целостность и конфиденциальность данных.
Iron реализует концепцию sealed box: объект сериализуется, шифруется и снабжается механизмом проверки целостности. В результате получается строка, которая:
Процесс состоит из двух этапов:
Таким образом, Iron объединяет конфиденциальность и защиту от модификации в одном формате.
Основные операции библиотеки — seal и
unseal.
import Iron from '@hapi/iron';
const password = 'super-secure-password';
const session = {
userId: 42,
role: 'admin',
permissions: ['read', 'write']
};
const sealed = await Iron.seal(session, password, {
ttl: 60 * 60 * 1000 // 1 час
});
Результатом будет строка, содержащая зашифрованный и подписанный контекст.
const unsealed = await Iron.unseal(sealed, password, {
ttl: 60 * 60 * 1000
});
console.log(unsealed.userId); // 42
Если строка была изменена или срок жизни истёк, операция завершится ошибкой.
В микросервисной архитектуре каждый сервис стремится быть автономным. Однако часто требуется передавать контекст пользователя, например:
Использование Iron позволяет передавать этот контекст без обращения к централизованной базе данных или Redis-сессиям.
Контекст может быть упакован в строку и отправлен в заголовке:
// gateway service
const contextToken = await Iron.seal({
userId: 10,
tenantId: 'acme',
scope: ['billing']
}, password);
fetch('https://orders.service.local/create', {
method: 'POST',
headers: {
'x-context': contextToken
}
});
На стороне сервиса:
const token = req.headers['x-context'];
const context = await Iron.unseal(token, password);
if (context.scope.includes('billing')) {
// разрешаем доступ
}
Таким образом каждый сервис получает полный набор необходимых данных без обращения к внешнему хранилищу.
Традиционные системы используют серверные сессии, которые требуют хранения состояния. Iron позволяет заменить их полностью статeless-подходом:
Это особенно полезно в системах с горизонтальным масштабированием, где синхронизация сессий становится узким местом.
Хотя Iron и JSON Web Token решают похожие задачи, их подход различается:
Ключевое отличие — уровень скрытия данных: Iron по умолчанию делает содержимое недоступным без расшифровки.
Передача контекста через Iron позволяет формировать единый объект состояния запроса:
{
user: {
id: 10,
name: 'Alex'
},
auth: {
role: 'editor'
},
request: {
correlationId: 'abc-123'
}
}
Такой объект может сопровождать запрос через все сервисы цепочки, не требуя повторной авторизации и дополнительных запросов к базе.
Контекст можно передавать не только через HTTP, но и через брокеры сообщений:
const message = {
payload: orderData,
context: await Iron.seal(userContext, password)
};
queue.publish('orders.created', message);
Консьюмер восстанавливает контекст:
const context = await Iron.unseal(message.context, password);
Это позволяет сохранять идентичность запроса в асинхронных потоках обработки.
Безопасность Iron полностью зависит от секретного ключа. В реальных системах важно учитывать:
Пример использования массива ключей:
const password = [
process.env.IRON_KEY_CURRENT,
process.env.IRON_KEY_PREVIOUS
];
Это позволяет расшифровывать старые токены после смены ключа.
Каждый sealed-объект может иметь срок жизни:
{
ttl: 5 * 60 * 1000 // 5 минут
}
После истечения времени попытка восстановления приведёт к ошибке. Это предотвращает повторное использование устаревших контекстов и снижает риск replay-атак.
API-шлюз формирует контекст один раз и передаёт его дальше:
Каждый сервис:
Контекст может включать correlationId, что
позволяет:
Несмотря на удобство, использование Iron для передачи контекста требует учёта ограничений:
Тем не менее в системах, ориентированных на stateless-архитектуру, эти ограничения компенсируются снижением инфраструктурной сложности.
Iron чаще всего используется:
В таких сценариях он выступает как слой безопасной сериализации состояния, позволяющий отказаться от базы данных как источника сессионного контекста.