В контексте использования Iron токен представляет собой зашифрованную и подписанную структуру, которая после сериализации проходит несколько стадий преобразования: сериализация данных, шифрование, добавление HMAC-подписи и кодирование в строку. Любое нарушение на одном из этапов приводит к невозможности корректного восстановления исходного объекта.
На практике повреждение токена возникает по следующим причинам:
Iron строго проверяет целостность данных, поэтому даже минимальное изменение делает токен недействительным.
Библиотека @hapi/iron не пытается «восстановить» повреждённые данные. Любая ошибка целостности приводит к выбросу исключения при попытке распаковки.
Основные классы ошибок:
Bad HMAC Value — нарушена подпись, данные были
измененыDecryption failed — невозможность расшифровки
содержимогоBad version number — несовместимый формат токенаInvalid structure — токен повреждён или обрезанBad input — некорректная строка на входеЭта модель поведения является частью безопасности: отсутствие «частичного восстановления» предотвращает использование потенциально модифицированных данных.
Любая операция извлечения данных из токена должна быть защищена конструкцией try/catch, поскольку Iron работает через исключения.
import Iron from '@hapi/iron';
const password = 'very-secure-password';
async function decodeToken(sealed) {
try {
const unsealed = await Iron.unseal(
sealed,
password,
Iron.defaults
);
return unsealed;
} catch (err) {
return null;
}
}
В данном случае любое повреждение токена приводит к безопасному
возврату null, без раскрытия внутренних деталей ошибки.
Более строгая обработка требует различения причин сбоя. Iron
выбрасывает стандартные ошибки, которые можно анализировать по
err.message.
async function decodeToken(sealed) {
try {
return await Iron.unseal(sealed, password, Iron.defaults);
} catch (err) {
switch (err.message) {
case 'Bad HMAC Value':
// возможная подмена токена
break;
case 'Decryption failed':
// повреждение или неверный ключ
break;
case 'Bad version number':
// устаревший формат
break;
default:
// неизвестное повреждение
break;
}
return null;
}
}
Такая структура позволяет отделить технические сбои от потенциальных атак.
Повреждённые токены не всегда случайны. В ряде случаев они могут быть результатом попытки повторной отправки модифицированных данных. Поэтому важно минимизировать возможность их повторного анализа.
Практика:
catch (err) {
// логирование только на сервере
console.error('Token error');
return null;
}
При использовании Iron для сессий через cookies повреждённый токен часто приходит от клиента автоматически. В этом случае обработка должна быть прозрачной для пользователя.
export async function authMiddleware(req, res, next) {
const token = req.cookies.session;
if (!token) {
req.user = null;
return next();
}
try {
req.user = await Iron.unseal(token, password, Iron.defaults);
} catch {
req.user = null;
res.clearCookie('session');
}
next();
}
Удаление повреждённого токена предотвращает бесконечные циклы ошибок при повторных запросах.
Хотя Iron уже выполняет проверку целостности, предварительная валидация строки снижает количество лишних исключений.
function isLikelyToken(str) {
return typeof str === 'string' &&
str.length > 20 &&
/^[A-Za-z0-9\-_\.]+$/.test(str);
}
Такая фильтрация не гарантирует валидность, но отсеивает явно
испорченные данные до вызова unseal.
В реальных системах возможны массовые сбои токенов после:
В таких случаях корректная стратегия — полная инвалидизация сессий.
async function safeUnseal(sealed) {
try {
return await Iron.unseal(sealed, newPassword, Iron.defaults);
} catch {
return null;
}
}
При этом клиентская часть должна быть рассчитана на повторную авторизацию без восстановления старого состояния.
Хотя детали ошибок не должны передаваться наружу, серверная диагностика остаётся важной частью эксплуатации.
Рекомендуемая структура логов:
catch (err) {
logger.error({
type: 'iron_token_error',
message: err.message,
stack: err.stack
});
return null;
}
При этом важно избегать сохранения самого токена в логах, так как он может содержать чувствительные данные даже в зашифрованном виде.
Iron гарантирует целостность, но не защищает от логических ошибок приложения. Например, корректный токен может содержать устаревшие данные пользователя.
Поэтому после успешной распаковки часто добавляется дополнительная проверка:
const session = await Iron.unseal(token, password, Iron.defaults);
if (session.exp && Date.now() > session.exp) {
return null;
}
Таким образом разделяются два уровня проверки:
Смена или рассинхронизация секретного ключа приводит к массовому
появлению ошибок Bad HMAC Value и
Decryption failed. В этом случае все ранее выданные токены
становятся недействительными.
Типичная реакция системы:
catch (err) {
if (err.message === 'Bad HMAC Value') {
res.clearCookie('session');
}
return null;
}
Иногда токен формально проходит проверку структуры, но данные внутри оказываются некорректными из-за ошибок сериализации.
Пример защиты:
const session = await Iron.unseal(token, password, Iron.defaults);
if (!session || typeof session !== 'object') {
return null;
}
if (!session.userId) {
return null;
}
Iron гарантирует криптографическую корректность, но не структуру бизнес-данных.
Стабильная работа с Iron в условиях повреждённых токенов строится на нескольких принципах:
Такая модель делает систему устойчивой к случайным повреждениям и целенаправленным попыткам вмешательства в структуру токенов.