bcrypt в Node.js: хеширование и соление паролей

Одним из лучших способов надёжно хранить пароли является их хеширование с добавлением соли. Процесс преобразует обычный пароль в уникальное значение, обратное восстановление которого затруднено. Библиотека bcrypt в Node.js позволяет выполнять эти операции просто и безопасно.
Что такое хеширование паролей?
Хеширование паролей — это применение хеш-функции к текстовому паролю для получения фиксированного уникального значения, называемого хешем. Хеш-функция детерминирована: один и тот же ввод даёт один и тот же результат. Примеры алгоритмов: bcrypt, scrypt, Argon2 и SHA-семейство (SHA не рекомендуется для паролей без соли и адаптивного фактора).
Ключевая проблема детерминированности — уязвимость к атаке по таблице предварительно вычисленных хешей (rainbow tables). Атакующий может заранее посчитать хеши для популярных паролей и быстро их сопоставить. Эту слабость устраняет добавление соли.
Что такое соль и зачем она нужна?
Соль — это случайная строка, добавляемая к паролю перед хешированием. Благодаря соли один и тот же пароль будет давать разные хеши при каждом создании учётной записи. Даже при утечке базы данных атакующему придётся тратить ресурсы на каждую запись отдельно.
Короткое определение: соль делает хеш уникальным для каждой записи и предотвращает массовое использование предвычисленных таблиц.
Как использовать bcrypt в Node.js
Ниже — пошаговая инструкция с примерами кода. Всё представлено на JavaScript для Node.js.
Шаг 1: установка bcrypt
Установите пакет через npm или yarn.
npm install bcryptили
yarn add bcryptШаг 2: импорт библиотеки
В начале файла импортируйте bcrypt.
const bcrypt = require("bcrypt");Шаг 3: генерация соли
Вызовите bcrypt.genSalt(), чтобы получить соль. Этот метод принимает целое число — фактор стоимости (cost factor), который определяет, сколько вычислительной работы требуется для хеширования. Чем выше фактор, тем медленнее работа и тем сложнее подобрать пароль перебором.
Обычно фактор стоимости выбирают в диапазоне 5–15; часто рекомендуют 10 как разумный баланс между безопасностью и производительностью.
Пример с колбэком:
bcrypt.genSalt(10, (err, salt) => {
if (err) throw err;
// используйте salt для хеширования
});Шаг 4: хеширование пароля
Передавайте исходный пароль и соль в bcrypt.hash(). Можно сначала сгенерировать соль, а затем хешировать, либо воспользоваться упрощённой функцией, которая делает всё сразу.
Генерация соли и хеширование в колбэке:
bcrypt.genSalt(10, (err, salt) => {
if (err) throw err;
bcrypt.hash(plaintextPassword, salt, (err, hash) => {
if (err) throw err;
// Сохраните hash в базе данных
});
});Хеширование с авто-генерацией соли (в один вызов):
bcrypt.hash(plaintextPassword, 10, (err, hash) => {
if (err) throw err;
// Сохраните hash в базе данных
});Шаг 5: сравнение паролей для аутентификации
При логине вы сравниваете введённый пользователем пароль и хеш из базы с помощью bcrypt.compare(). Функция возвращает true, если пароль соответствует хешу.
bcrypt.compare(plaintextPassword, hash, (err, result) => {
if (err) throw err;
if (result) {
// Пароль верный — выполняем аутентификацию
} else {
// Неверный пароль
}
});Использование async/await
bcrypt поддерживает промисы, поэтому можно писать асинхронно через async/await.
async function hashPassword(plaintextPassword) {
const hash = await bcrypt.hash(plaintextPassword, 10);
// Сохраните hash в базе данных
return hash;
}
async function comparePassword(plaintextPassword, hash) {
const result = await bcrypt.compare(plaintextPassword, hash);
return result; // true или false
}Использование промисов then/catch
function hashPassword(plaintextPassword) {
bcrypt.hash(plaintextPassword, 10)
.then(hash => {
// Сохраните hash в базе данных
})
.catch(err => {
console.error(err);
});
}
function comparePassword(plaintextPassword, hash) {
return bcrypt.compare(plaintextPassword, hash)
.then(result => result)
.catch(err => {
console.error(err);
return false;
});
}Когда хеширование с солью может не сработать
Important: хеширование и соль — не панацея. Примеры ситуаций, когда этого недостаточно:
- Слабые пароли пользователей (password123, qwerty) всё ещё уязвимы к словарным атакам, даже если хранятся хеши.
- Повторное использование паролей между сервисами — утечка с одного сайта позволяет атаковать другой.
- Утечка непротектированных резервных копий базы данных или ключей приложения (например, «pepper») снижает преимущества хеширования.
- Неправильная конфигурация фактора стоимости (слишком низкий) делает перебор быстрым.
Альтернативные подходы
Если вы оцениваете риск и производительность, рассмотрите другие алгоритмы:
- Argon2 — современный и рекомендуемый алгоритм для хеширования паролей (заявлен как победитель конкурса PHC).
- scrypt — устойчив к специализированному аппаратному ускорению, хорош для высоких затрат памяти.
- PBKDF2 — широко поддерживается и стандартизирован, но требует правильной настройки параметров.
Выбор зависит от требований к производительности, доступному объёму памяти и уровня угроз.
Мини-методология внедрения (шаги для команды)
- Выберите алгоритм и фактор стоимости (начните с 10 для bcrypt или рекомендаций по Argon2).
- Реализуйте хеширование при создании/смене пароля.
- При аутентификации используйте безопасное сравнение (bcrypt.compare).
- Внедрите проверку силы пароля и правила блокировки после попыток входа.
- План миграции: храните маркер алгоритма и параметров рядом с хешем, чтобы постепенно переходить на новый алгоритм.
- Не храните пароли в логах и не отправляйте их в сторонние сервисы.
Фактбокс
- Рекомендуемый фактор стоимости bcrypt: обычно 5–15; часто используют 10.
- Соль: генерируется автоматически bcrypt и обычно имеет достаточно длины (встроена в формат хеша).
- Хеши — необратимы при правильной настройке, но их можно проверить только подбором.
Рекомендации по безопасности
- Всегда проверяйте силу пароля (длина, набор символов, черный список общих паролей).
- Введите ограничения по числу попыток входа и задержки (rate limiting, exponential backoff).
- Храните в базе данных только хеши и служебные метаданные (алгоритм, фактор).
- Рассмотрите использование «pepper» — глобальной секретной строки, хранящейся вне БД (например, в менеджере секретов). Это увеличит безопасность при утечке базы, но усложняет управление.
- Шифруйте резервные копии базы данных и используйте контроль доступа по ролям.
Роли и чек-листы
Разработчик:
- Реализовать хеширование при регистрации и смене пароля.
- Использовать bcrypt.compare для проверки.
- Не логировать пароли.
Оператор/DevOps:
- Хранить секреты (pepper) в безопасном хранилище.
- Настроить мониторинг попыток входа и алерты на аномалии.
Менеджер безопасности:
- Утвердить минимальные требования к паролям и фактору стоимости.
- Планировать регулярный аудит и тесты на проникновение.
Критерии приёмки
- Новые пароли сохраняются в базе только в виде хешей.
- Для каждой записи указан алгоритм/параметры хеширования.
- Проходят тесты сравнения паролей (unit tests) и интеграционные проверки входа.
- При попытке логина с неверным паролем счётчик попыток увеличивается и при достижении порога вводится задержка/блокировка.
Краткое резюме
- bcrypt упрощает безопасное хеширование паролей в Node.js.
- Соль делает хеши уникальными и защищает от предвычисленных таблиц.
- Важно комбинировать хеширование с проверкой силы пароля, ограничением попыток и хранением секретов.
Сводка для быстрой ссылки:
- Используйте bcrypt или современную альтернативу (Argon2) с адекватным фактором стоимости.
- Не забывайте проверять сложность пароля и лимитировать попытки входа.
- Планируйте миграцию алгоритмов и храните параметры вместе с хешем.
Похожие материалы
Как устроить идеальную вечеринку для просмотра ТВ
Как распаковать несколько RAR‑файлов сразу
Приватный просмотр в Linux: как и зачем
Windows 11 не видит iPod — способы исправить
PS5: как настроить игровые пресеты