В веб-приложениях защищённые маршруты опираются на механизм аутентификации и авторизации, где сервер должен уметь проверять подлинность пользователя и целостность его сессии. В экосистеме Node.js одним из инструментов, применяемых для криптографического «запечатывания» данных, выступает библиотека @hapi/iron. Она позволяет сериализовать объект в строку с проверкой целостности и шифрованием, а затем безопасно восстанавливать его на сервере.
В контексте интеграционного тестирования защищённых маршрутов Iron часто используется косвенно — через сессионные cookies, JWT-обёртки или механизмы хранения состояния пользователя. Это требует тщательной подготовки тестового окружения, поскольку любые ошибки в процессе sealing/unsealing приводят к невозможности воспроизвести корректную авторизацию в тестах.
Типичная архитектура защищённого маршрута включает следующие элементы:
Пример упрощённой логики:
import Iron from '@hapi/iron';
const password = process.env.IRON_PASSWORD;
async function authMiddleware(req, res, next) {
const sealed = req.cookies.session;
if (!sealed) {
return res.status(401).send('Unauthorized');
}
try {
const session = await Iron.unseal(sealed, password, Iron.defaults);
req.user = session.user;
next();
} catch (e) {
return res.status(401).send('Invalid session');
}
}
Здесь Iron выполняет критическую функцию: гарантирует, что данные сессии не были изменены клиентом.
Интеграционные тесты проверяют работу системы целиком: маршруты, middleware, работу с cookies, сериализацию сессий и криптографические операции.
Основные задачи таких тестов:
Ключевая сложность — корректное формирование sealed-данных, совместимых с Iron.
Для интеграционных тестов обычно используются:
IRON_PASSWORDВажно: пароль для Iron должен быть одинаковым в приложении и тестах, иначе расшифровка будет невозможна.
process.env.IRON_PASSWORD = 'test-iron-password';
Чтобы протестировать защищённый маршрут, необходимо создать валидную сессию, зашифрованную Iron.
import Iron from '@hapi/iron';
const password = process.env.IRON_PASSWORD;
export async function createSessionCookie(user) {
const session = {
user: {
id: user.id,
role: user.role
}
};
const sealed = await Iron.seal(session, password, Iron.defaults);
return `session=${sealed}`;
}
Эта функция используется в тестах для имитации авторизованного пользователя.
Первый слой проверки — защита от неавторизованных запросов.
import request from 'supertest';
import app from '../app';
test('доступ к защищённому маршруту без сессии запрещён', async () => {
const res = await request(app)
.get('/api/profile');
expect(res.status).toBe(401);
});
Здесь важно корректно сформировать Iron-сессию и передать её в cookie.
import request from 'supertest';
import app from '../app';
import { createSessionCookie } from './helpers/session';
test('доступ к защищённому маршруту с валидной сессией разрешён', async () => {
const cookie = await createSessionCookie({
id: 1,
role: 'user'
});
const res = await request(app)
.get('/api/profile')
.set('Cookie', cookie);
expect(res.status).toBe(200);
expect(res.body.user.id).toBe(1);
});
Iron защищает данные от модификации, поэтому изменение даже одного символа должно приводить к ошибке.
test('поддельная сессия отклоняется', async () => {
const fakeCookie = 'session=invalid-token';
const res = await request(app)
.get('/api/profile')
.set('Cookie', fakeCookie);
expect(res.status).toBe(401);
});
Дополнительно важно проверить случаи, когда данные выглядят корректно, но повреждены криптографически.
test('повреждённая Iron-сессия отклоняется', async () => {
const cookie = await createSessionCookie({ id: 1 });
const corrupted = cookie + 'a';
const res = await request(app)
.get('/api/profile')
.set('Cookie', corrupted);
expect(res.status).toBe(401);
});
Защищённые маршруты часто зависят не только от факта аутентификации, но и от роли.
test('доступ запрещён для пользователя без прав администратора', async () => {
const cookie = await createSessionCookie({
id: 2,
role: 'user'
});
const res = await request(app)
.delete('/api/admin/users/1')
.set('Cookie', cookie);
expect(res.status).toBe(403);
});
Особое внимание уделяется тому, как Iron интегрирован в middleware слой.
Типовые ошибки:
Iron.defaultsДля выявления таких проблем добавляются тесты уровня инфраструктуры:
test('Iron корректно шифрует и расшифровывает сессию', async () => {
const original = { user: { id: 10 } };
const sealed = await Iron.seal(original, process.env.IRON_PASSWORD, Iron.defaults);
const unsealed = await Iron.unseal(sealed, process.env.IRON_PASSWORD, Iron.defaults);
expect(unsealed).toEqual(original);
});
Даже небольшое отличие в параметрах шифрования приводит к невозможности декодирования данных.
Если система включает TTL, тесты могут падать из-за истечения срока.
Мокирование снижает ценность интеграционного теста, так как убирает криптографический слой.
Некорректный формат заголовка Cookie приводит к ложным
ошибкам аутентификации.
Эффективный набор интеграционных тестов защищённых маршрутов строится слоями:
Такая структура позволяет выявить ошибки не только в бизнес-логике, но и в криптографическом и транспортном уровнях приложения.