Express.js: middleware для хеширования паролей

В веб-приложениях обработка паролей требует обязательного применения криптографического хеширования до их сохранения. В экосистеме Node.js одной из наиболее распространённых библиотек для этой задачи является bcrypt.js — реализация алгоритма bcrypt, адаптированная для JavaScript и работающая как в серверной среде Node.js, так и в браузере.

Основная задача middleware в Express.js в контексте паролей — перехват входящих данных пользователя и преобразование открытого пароля в безопасный хеш перед сохранением в базу данных.


Установка bcrypt.js и базовая настройка

Библиотека устанавливается стандартным способом через npm:

npm install bcryptjs

После установки доступ к функционалу осуществляется через импорт:

const bcrypt = require('bcryptjs');

Принцип работы bcrypt.js

Алгоритм bcrypt основан на адаптивной функции хеширования, которая включает:

  • соль (salt), генерируемую для каждого пароля
  • параметр cost factor (число раундов вычислений)
  • одностороннее преобразование пароля в хеш

Ключевое свойство — невозможность восстановления исходного пароля из хеша.


Middleware в Express.js: концепция применения

Middleware в Express.js представляет собой функцию, которая получает доступ к объектам запроса и ответа, а также управляет потоком выполнения через next().

В контексте хеширования паролей middleware используется как промежуточный слой между получением данных от клиента и сохранением их в базу данных.


Простейшая реализация middleware для хеширования

const bcrypt = require('bcryptjs');

const hashPasswordMiddleware = async (req, res, next) => {
    try {
        const password = req.body.password;

        if (!password) {
            return res.status(400).json({ message: 'Пароль отсутствует' });
        }

        const saltRounds = 10;
        const hashedPassword = await bcrypt.hash(password, saltRounds);

        req.body.password = hashedPassword;

        next();
    } catch (error) {
        return res.status(500).json({ message: 'Ошибка хеширования пароля' });
    }
};

Интеграция middleware в маршрут Express.js

Middleware подключается на уровне маршрута:

const express = require('express');
const app = express();

app.use(express.json());

app.post('/register', hashPasswordMiddleware, async (req, res) => {
    const user = {
        email: req.body.email,
        password: req.body.password
    };

    // сохранение пользователя в базу данных
    res.status(201).json({ message: 'Пользователь создан' });
});

Генерация соли и управление cost factor

Параметр salt rounds определяет сложность вычислений. Чем выше значение, тем медленнее вычисляется хеш, но выше безопасность.

const saltRounds = 12;

Альтернативный способ явной генерации соли:

const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(password, salt);

Проверка пароля при авторизации

Хеширование используется только при создании или обновлении пароля. При логине выполняется сравнение:

const isMatch = await bcrypt.compare(plainPassword, hashedPassword);

if (!isMatch) {
    return res.status(401).json({ message: 'Неверный пароль' });
}

Разделение middleware: регистрация и авторизация

В реальных приложениях логика хеширования и проверки разделяется:

  • middleware для регистрации → bcrypt.hash
  • сервис авторизации → bcrypt.compare

Пример структуры:

// middleware
const hashPassword = async (req, res, next) => {
    req.body.password = await bcrypt.hash(req.body.password, 10);
    next();
};

// login service
const validatePassword = async (password, hash) => {
    return await bcrypt.compare(password, hash);
};

Работа с базами данных и автоматическое хеширование

При использовании ORM (например, Sequelize или Mongoose) хеширование часто переносится на уровень модели.

Пример с Mongoose:

const userSchema = new mongoose.Schema({
    email: String,
    password: String
});

userSchema.pre('save', async function (next) {
    if (!this.isModified('password')) return next();

    this.password = await bcrypt.hash(this.password, 10);
    next();
});

Такой подход уменьшает зависимость от middleware Express.js и повышает целостность логики.


Ошибки при использовании bcrypt.js в middleware

На практике часто встречаются следующие проблемы:

  • повторное хеширование уже хешированного пароля
  • отсутствие проверки существования поля password
  • блокировка event loop при слишком высоком salt rounds
  • хранение оригинального пароля в логах или временных объектах

Асинхронность и производительность

bcrypt.js использует CPU-интенсивные операции. При высокой нагрузке важно:

  • ограничивать salt rounds (обычно 10–12)
  • не выполнять массовое хеширование в одном потоке
  • при необходимости выносить операции в worker threads

Сравнение синхронных и асинхронных методов

Асинхронный вариант:

await bcrypt.hash(password, 10);

Синхронный вариант:

bcrypt.hashSync(password, 10);

Синхронные методы блокируют event loop и не подходят для production-среды с высокой нагрузкой.


Безопасная архитектура middleware слоя

Правильная организация слоя Express.js обычно включает:

  • middleware валидации входных данных
  • middleware хеширования паролей
  • сервисный слой бизнес-логики
  • слой доступа к данным (database layer)

Хеширование не должно смешиваться с бизнес-логикой или контроллерами напрямую.


Типовой поток обработки регистрации пользователя

  1. Получение данных запроса
  2. Валидация email и пароля
  3. Middleware хеширования пароля
  4. Создание записи пользователя
  5. Сохранение в базе данных

Практическая структура проекта

/controllers
/middleware
/services
/models
/routes

Middleware для bcrypt.js обычно располагается отдельно:

/middleware/hashPassword.js

Обработка обновления пароля

При смене пароля важно повторно применять хеширование:

app.put('/user/password', async (req, res) => {
    const newPassword = req.body.password;

    const hashed = await bcrypt.hash(newPassword, 10);

    // обновление в базе
});

Криптографические особенности bcrypt.js

Алгоритм bcrypt включает:

  • Blowfish cipher
  • встроенную соль
  • адаптивную сложность вычислений

Это делает его устойчивым к brute-force атакам по сравнению с устаревшими алгоритмами (например, MD5 или SHA1).


Рекомендации по применению в Express.js

  • использовать только асинхронные методы bcrypt
  • централизовать хеширование в middleware или модели
  • не хранить и не логировать пароли в открытом виде
  • выбирать salt rounds исходя из нагрузки сервера
  • отделять регистрацию и авторизацию по разным слоям логики