При работе с защищёнными структурами данных в формате Iron основная уязвимая зона — процесс восстановления исходного объекта из сериализованной и зашифрованной строки. Любое отклонение от ожидаемого формата приводит к ошибкам десериализации или, в худшем случае, к некорректной интерпретации данных.
Невалидные токены в контексте Iron можно разделить на несколько категорий:
Каждый тип ошибки должен проверяться отдельно, поскольку механизм
unseal в Iron реагирует на них по-разному.
В библиотеке @hapi/iron основной операцией является
восстановление данных:
import Iron from '@hapi/iron';
const password = 'strong-password';
const sealed = await Iron.seal({ user: 'admin', role: 'root' }, password, Iron.defaults);
const unsealed = await Iron.unseal(sealed, password, Iron.defaults);
Любое отклонение от корректного значения sealed должно
приводить к исключению. Это поведение используется как основа для
тестирования.
Наиболее частый сценарий — обрезанный или изменённый токен, например при ошибке хранения или передачи через URL.
import Iron from '@hapi/iron';
const password = 'strong-password';
async function testTruncatedToken() {
const valid = await Iron.seal({ id: 1 }, password, Iron.defaults);
const corrupted = valid.slice(0, valid.length - 10);
try {
await Iron.unseal(corrupted, password, Iron.defaults);
throw new Error('Токен не должен быть валидным');
} catch (err) {
console.log('Ошибка корректно обработана:', err.message);
}
}
Ожидаемое поведение — исключение на этапе проверки подписи или структуры.
Манипуляции с содержимым токена позволяют проверить устойчивость MAC-подписи.
async function testModifiedToken() {
const valid = await Iron.seal({ id: 42 }, password, Iron.defaults);
const modified = valid.replace(/a/g, 'b');
try {
await Iron.unseal(modified, password, Iron.defaults);
} catch (err) {
console.log('Обнаружено изменение токена');
}
}
Даже минимальная модификация должна нарушать целостность, поскольку подпись пересчитывается при проверке.
Один из критических сценариев — попытка расшифровки корректного токена другим секретом.
async function testWrongPassword() {
const token = await Iron.seal({ id: 100 }, 'correct-password', Iron.defaults);
try {
await Iron.unseal(token, 'wrong-password', Iron.defaults);
} catch (err) {
console.log('Неверный ключ отклонён');
}
}
Этот тест подтверждает, что данные надёжно привязаны к секрету.
Iron использует сериализацию, основанную на безопасном представлении строки. Нарушение кодировки часто возникает при ручной обработке токена.
async function testInvalidEncoding() {
const token = await Iron.seal({ role: 'user' }, password, Iron.defaults);
const broken = token.replace(/[-_]/g, '+');
try {
await Iron.unseal(broken, password, Iron.defaults);
} catch (err) {
console.log('Ошибка кодировки обработана');
}
}
Подобные изменения часто встречаются при ошибках проксирования или неправильной обработке URL-safe Base64.
Иногда токен сохраняет структуру, но теряет часть данных внутри зашифрованного payload.
async function testPartiallyCorruptedPayload() {
const token = await Iron.seal({ session: { id: 1, permissions: ['read', 'write'] } }, password, Iron.defaults);
const parsed = token.split('*');
parsed[parsed.length - 1] = parsed[parsed.length - 1].slice(5);
const corrupted = parsed.join('*');
try {
await Iron.unseal(corrupted, password, Iron.defaults);
} catch (err) {
console.log('Повреждение payload обнаружено');
}
}
Такие тесты полезны для проверки устойчивости парсера и криптографического слоя.
Если система предполагает одноразовые токены, необходимо тестировать сценарии replay-атаки.
const usedTokens = new Set();
async function verifyOnce(token) {
if (usedTokens.has(token)) {
throw new Error('Повторное использование токена запрещено');
}
const data = await Iron.unseal(token, password, Iron.defaults);
usedTokens.add(token);
return data;
}
Тестирование включает повторную передачу одного и того же значения.
Iron-токен имеет строгую внутреннюю структуру. Любое нарушение количества сегментов приводит к немедленной ошибке.
async function testInvalidStructure() {
const invalid = 'abc.def.ghi';
try {
await Iron.unseal(invalid, password, Iron.defaults);
} catch (err) {
console.log('Структура токена некорректна');
}
}
Такие тесты выявляют ошибки на уровне парсинга до криптографической проверки.
Для автоматизации проверки устойчивости системы применяются генераторы повреждённых токенов.
function mutate(token) {
const mutations = [
t => t + 'a',
t => t.slice(1),
t => t.replace(/\./g, ''),
t => t.split('').reverse().join(''),
];
return mutations.map(fn => fn(token));
}
async function runFuzzTest() {
const base = await Iron.seal({ test: true }, password, Iron.defaults);
const cases = mutate(base);
for (const c of cases) {
try {
await Iron.unseal(c, password, Iron.defaults);
} catch (e) {
console.log('Ожидаемая ошибка:', e.message);
}
}
}
Такой подход позволяет выявить нестандартные сценарии повреждения данных.
Важно различать типы исключений:
Разделение этих случаев позволяет точнее диагностировать источник проблемы в продакшене.
Некорректные длины строки часто возникают при обрезке HTTP-заголовков или ограничениях cookie.
async function testBoundaryLengths() {
const token = await Iron.seal({ data: 'x'.repeat(1000) }, password, Iron.defaults);
const shortened = token.substring(0, 50);
try {
await Iron.unseal(shortened, password, Iron.defaults);
} catch (err) {
console.log('Граничная длина обработана корректно');
}
}
Иногда токен повреждается при неверной обработке как бинарного буфера.
async function testBinaryCorruption() {
const token = await Iron.seal({ id: 7 }, password, Iron.defaults);
const corrupted = Buffer.from(token);
corrupted[5] = 0x00;
try {
await Iron.unseal(corrupted.toString(), password, Iron.defaults);
} catch (err) {
console.log('Бинарное повреждение выявлено');
}
}
Полноценная стратегия тестирования невалидных токенов в Iron строится на сочетании:
Такая комбинация позволяет обеспечить предсказуемое поведение
unseal при любых нарушениях входных данных и исключить
возможность частичного восстановления или ложной интерпретации
данных.