Многофакторная аутентификация (MFA) строится на сочетании независимых факторов, относящихся к разным категориям:
Классическая схема включает пароль как базовый фактор и дополнительный механизм подтверждения (одноразовый код, push-уведомление, аппаратный ключ). Несмотря на добавление новых уровней защиты, пароль остаётся фундаментом, а значит, его хранение и обработка должны быть максимально безопасными.
Пароли подвержены множеству атак:
Хранение паролей в открытом виде или с использованием быстрых хеш-функций (например, SHA-256 без соли) делает систему уязвимой при утечке базы данных.
Требования к безопасному хранению:
Библиотека bcrypt.js реализует алгоритм bcrypt на чистом
JavaScript и применяется для:
Ключевые свойства bcrypt:
Пример хеширования:
const bcrypt = require('bcryptjs');
const password = 'user_password';
const saltRounds = 12;
const hash = bcrypt.hashSync(password, saltRounds);
Пример проверки:
const isMatch = bcrypt.compareSync('user_password', hash);
В контексте MFA хеширование паролей выполняет несколько критически важных функций:
Даже при наличии второго фактора пароль остаётся первой линией защиты. Его компрометация снижает общую безопасность системы. bcrypt минимизирует ущерб от утечки базы:
Факторы MFA должны быть независимыми. Хеширование обеспечивает, что:
Даже если атакующий получает доступ к хешам:
Типичный процесс аутентификации с MFA:
Пользователь вводит логин и пароль
Сервер:
При успешной проверке:
Доступ предоставляется только после подтверждения второго фактора
Кодовая схема:
const bcrypt = require('bcryptjs');
function authenticate(passwordInput, storedHash) {
if (!bcrypt.compareSync(passwordInput, storedHash)) {
return false;
}
return true; // переход ко второму фактору
}
Параметр saltRounds определяет сложность
хеширования:
Баланс достигается между:
Рекомендация: выбирать значение, при котором хеширование занимает 100–500 мс на сервере.
Наличие MFA не устраняет необходимость защищённого хеширования:
При этом слабое хеширование делает систему ещё более уязвимой:
Дополнительные меры:
bcrypt остаётся ядром этих механизмов, обеспечивая безопасное хранение.
Для серверов с высокой нагрузкой предпочтительно использовать асинхронные методы:
bcrypt.hash(password, saltRounds, (err, hash) => {
// сохранение хеша
});
bcrypt.compare(passwordInput, storedHash, (err, result) => {
// проверка
});
Или через Promises:
const hash = await bcrypt.hash(password, 12);
const match = await bcrypt.compare(passwordInput, hash);
Это предотвращает блокировку event loop в Node.js.
Даже при использовании современных подходов:
пароли продолжают использоваться как fallback-механизм. bcrypt обеспечивает:
Распространённые проблемы:
saltRoundsbcrypt.compareКорректная реализация требует строгого соблюдения модели:
bcrypt — намеренно медленный алгоритм. При масштабировании:
Это позволяет сохранить безопасность без деградации UX.
bcrypt позволяет увеличивать cost factor со временем:
Пример:
if (bcrypt.getRounds(storedHash) < 12) {
const newHash = await bcrypt.hash(passwordInput, 12);
// обновить в базе
}
Такой подход особенно важен в долгоживущих системах с MFA.