iron.seal

Iron — это библиотека для безопасной сериализации, шифрования и подписи данных в JavaScript. Она широко используется в экосистеме Node.js, особенно в проектах, где требуется надёжная защита сессий, токенов и любых структурированных данных, передаваемых между клиентом и сервером.

Главная идея Iron заключается в превращении произвольного JavaScript-объекта в строку, которая одновременно:

  • зашифрована (конфиденциальность данных)
  • подписана (защита от подделки)
  • имеет защиту от повторного воспроизведения (replay protection)
  • может иметь ограниченный срок жизни

Архитектура Iron-строки

Результат работы Iron — это компактная строка, содержащая несколько уровней защиты:

  • сериализованный JSON
  • HMAC-подпись
  • шифрование (AES)
  • метаданные (время, salt, id ключа)

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


Метод iron.seal

Назначение

iron.seal используется для упаковки (seal) объекта в защищённую строку.

Сигнатура:

Iron.seal(object, options, callback)

или промис-версия:

await Iron.seal(object, options)

Базовый пример использования

import Iron from '@hapi/iron';

const obj = {
    userId: 42,
    role: 'admin'
};

const password = 'super-secure-password';

const sealed = await Iron.seal(obj, {
    password,
    ttl: 60 * 60 * 1000 // 1 час
});

console.log(sealed);

Результат — строка, содержащая зашифрованный объект.


Расшифровка через iron.unseal

Чтобы восстановить исходный объект, используется unseal:

const unsealed = await Iron.unseal(sealed, {
    password,
    ttl: 60 * 60 * 1000
});

console.log(unsealed);

Если пароль неверный или срок действия истёк — операция завершится ошибкой.


Конфигурация параметров

password

Ключевой параметр безопасности.

password: 'very-long-random-string'

Требования:

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

ttl (Time To Live)

Определяет время жизни зашифрованного объекта.

ttl: 1000 * 60 * 5 // 5 минут

После истечения времени unseal выдаст ошибку.


encryption

Iron использует симметричное шифрование (AES-256-GCM). Это обеспечивает:

  • конфиденциальность данных
  • защиту от изменения ciphertext

integrity protection

Каждый sealed-объект содержит HMAC-подпись, которая проверяет:

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

Внутренний процесс seal

Процесс упаковки проходит несколько этапов:

1. Сериализация

Объект преобразуется в JSON:

JSON.stringify(object)

2. Генерация ключей

Из password выводятся криптографические ключи:

  • encryption key
  • integrity key

3. Шифрование

Данные шифруются AES:

plaintext → ciphertext

4. Подпись

Создаётся HMAC:

HMAC(ciphertext + metadata)

5. Финальная сборка

Все части кодируются в безопасный формат (base64url-like строка).


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

При расшифровке выполняется обратная проверка:

  1. разбор строки
  2. проверка подписи
  3. проверка TTL
  4. расшифровка данных
  5. парсинг JSON

Если хотя бы один шаг не проходит — выбрасывается ошибка.


Обработка ошибок

Типичные ошибки:

Bad HMAC value

Возникает при:

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

Expired seal

TTL истёк:

ttl: 1000 * 60

Cannot unseal

Обобщённая ошибка:

  • неверный формат
  • несовместимые версии
  • повреждение строки

Применение в реальных системах

1. Сессии пользователя

const session = await Iron.seal({
    userId: 123,
    scope: ['read', 'write']
}, { password });

Сервер может хранить только строку без базы данных.


2. JWT-альтернатива

В отличие от JWT:

  • нет разделения header.payload.signature
  • всё зашифровано целиком
  • payload недоступен для клиента

3. Защищённые cookies

reply.state('session', sealed, {
    isSecure: true,
    httpOnly: true
});

Отличия от JWT

JWT

  • данные видны (base64)
  • подпись отдельно
  • не шифруется по умолчанию

Iron

  • данные полностью зашифрованы
  • невозможно прочитать payload без ключа
  • встроенная защита от повторного использования

Производительность

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

  • быстрое шифрование AES
  • минимальные накладные расходы
  • подходит для high-load систем

Однако:

  • тяжелее JWT по CPU
  • не предназначен для массовых client-side операций

Безопасность и лучшие практики

Использование сильного password

crypto.randomBytes(32).toString('hex')

Регулярная ротация ключей

  • смена password каждые N дней
  • поддержка нескольких ключей для миграции

Ограничение TTL

Чем меньше TTL — тем безопаснее:

  • сессии: 15–60 минут
  • токены: до 24 часов
  • одноразовые данные: 1–5 минут

Поведение при изменении структуры объекта

Iron не зависит от схемы данных, но:

  • изменение структуры не влияет на расшифровку
  • старые sealed-строки остаются валидными до TTL

Особенности кодирования

Iron использует:

  • base64url-подобное кодирование
  • безопасные символы для URL и cookies
  • отсутствие padding-зависимостей

Совместимость версий

Важно учитывать:

  • sealed-строки могут быть несовместимы между major-версиями
  • рекомендуется хранить версию библиотеки на сервере

Типичные ошибки интеграции

Использование короткого password

Приводит к:

  • снижению криптостойкости
  • возможности перебора

Игнорирование TTL

  • создаёт риск replay-атак
  • увеличивает окно уязвимости

Хранение sensitive данных в sealed без необходимости

Iron защищает данные, но не отменяет здравый смысл архитектуры:

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