В типичной серверной архитектуре на Node.js аутентификация строится как отдельный модуль, инкапсулирующий работу с учетными данными, сессиями и токенами. При использовании Iron ключевая идея заключается в защите данных на уровне сериализации: объект пользователя не просто кодируется, а проходит процесс шифрования и подписи, что исключает возможность его изменения на клиентской стороне.
В рамках такого подхода аутентификационный слой обычно состоит из следующих компонентов:
Библиотека Iron из экосистемы Hapi используется для безопасного “запечатывания” (seal) и “распечатывания” (unseal) данных. Она обеспечивает криптографическую защиту объектов, которые могут храниться в cookie или токенах.
Установка:
npm install @hapi/iron
Базовое подключение в модуле:
import Iron from '@hapi/iron';
Основные методы:
Iron.seal(data, password, options) — шифрование
объектаIron.unseal(sealed, password, options) — восстановление
объектаБезопасность Iron напрямую зависит от качества секретного ключа. Он должен обладать достаточной энтропией и храниться вне исходного кода.
Пример конфигурации:
const ironOptions = {
password: process.env.IRON_PASSWORD,
encryption: {
saltBits: 256,
algorithm: 'aes-256-cbc',
iterations: 10000
}
};
Практика ротации ключей включает:
passwordПосле успешной аутентификации пользовательские данные упаковываются в защищённую строку:
const sessionData = {
id: user.id,
role: user.role
};
const sealed = await Iron.seal(sessionData, process.env.IRON_PASSWORD, ironOptions);
При последующих запросах данные извлекаются:
const unsealed = await Iron.unseal(cookieValue, process.env.IRON_PASSWORD, ironOptions);
Ключевой принцип заключается в том, что клиент никогда не видит исходную структуру данных — только зашифрованный контейнер.
В экосистеме Hapi логика аутентификации оформляется как плагин. Он отвечает за:
Пример структуры плагина:
export const authPlugin = {
name: 'authPlugin',
version: '1.0.0',
register: async function (server, options) {
server.auth.scheme('iron-scheme', () => {
return {
authenticate: async (request, h) => {
const cookie = request.state.session;
if (!cookie) {
throw Boom.unauthorized();
}
const credentials = await Iron.unseal(
cookie,
options.password,
options.ironOptions
);
return h.authenticated({ credentials });
}
};
});
}
};
Здесь схема iron-scheme определяет поведение проверки
каждого запроса.
После создания плагина он подключается к серверу:
import Hapi from '@hapi/hapi';
import { authPlugin } from './authPlugin.js';
const server = Hapi.server({
port: 3000
});
await server.register({
plugin: authPlugin,
options: {
password: process.env.IRON_PASSWORD,
ironOptions: {
encryption: {
saltBits: 256,
algorithm: 'aes-256-cbc',
iterations: 10000
}
}
}
});
Далее задается стратегия по умолчанию:
server.auth.default('iron-scheme');
Это означает, что каждый маршрут будет защищён, если не указано иное.
После регистрации плагина можно создавать разные уровни доступа. Например:
server.auth.strategy('admin', 'iron-scheme', {
validate: (credentials) => {
return { isValid: credentials.role === 'admin' };
}
});
Использование в маршруте:
server.route({
method: 'GET',
path: '/admin',
options: {
auth: 'admin'
},
handler: (request) => {
return { status: 'ok' };
}
});
Таким образом Iron выступает только как слой защиты данных, а логика доступа строится поверх него.
При работе с Iron важно учитывать потенциальные точки отказа:
Типовая обработка ошибок:
try {
const data = await Iron.unseal(token, password, options);
} catch (err) {
throw Boom.unauthorized('Invalid session');
}
Рекомендуемые меры безопасности:
Особое внимание уделяется тому, что Iron не заменяет полноценную систему аутентификации, а лишь обеспечивает криптографическую защиту данных внутри неё.