Использование в Koa

Обработка паролей в серверных приложениях требует строгого соблюдения принципов одностороннего преобразования данных. Библиотека password-hash реализует механизм хеширования с добавлением соли, позволяя безопасно хранить пароли в базе данных и проверять их без обратного восстановления исходного значения.

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


Установка и подключение библиотеки

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

npm install password-hash

Подключение в модуле сервиса:

const passwordHash = require('password-hash');

Библиотека предоставляет два ключевых метода:

  • generate(password, [options]) — создание хешированного значения
  • verify(password, hashedPassword) — проверка соответствия пароля и хеша

Основы работы алгоритма хеширования

Процесс генерации хеша включает:

  • добавление случайной соли;
  • применение криптографического алгоритма;
  • формирование строки, содержащей параметры хеширования.

Хранимое значение включает не только результат хеширования, но и параметры, необходимые для последующей проверки. Это исключает необходимость отдельного хранения соли.


Интеграция с архитектурой Koa

Koa строится вокруг асинхронных middleware, работающих с объектами ctx и next. Хеширование пароля логично выносится в сервисный слой, а взаимодействие с ним происходит через контроллеры.

Структура типичного слоя авторизации:

/services
  authService.js
/controllers
  authController.js
/routes
  authRoutes.js

Регистрация пользователя и хеширование пароля

При создании пользователя пароль не сохраняется в исходном виде. Выполняется его преобразование перед записью в базу данных.

Сервис авторизации

const passwordHash = require('password-hash');
const User = require('../models/User');

async function register(email, password) {
    const hashedPassword = passwordHash.generate(password);

    const user = new User({
        email,
        password: hashedPassword
    });

    await user.save();

    return user;
}

module.exports = {
    register
};

Ключевой момент заключается в том, что в базу попадает только результат функции generate, а исходный пароль не сохраняется нигде в системе.


Контроллер регистрации в Koa

const authService = require('../services/authService');

async function register(ctx) {
    const { email, password } = ctx.request.body;

    const user = await authService.register(email, password);

    ctx.status = 201;
    ctx.body = {
        id: user._id,
        email: user.email
    };
}

module.exports = {
    register
};

Объект ctx используется как единая точка доступа к данным запроса и формированию ответа.


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

При входе пользователя выполняется сравнение введённого пароля с сохранённым хешем.

const passwordHash = require('password-hash');
const User = require('../models/User');

async function login(email, password) {
    const user = await User.findOne({ email });

    if (!user) {
        return null;
    }

    const isValid = passwordHash.verify(password, user.password);

    if (!isValid) {
        return null;
    }

    return user;
}

module.exports = {
    login
};

Функция verify выполняет безопасное сравнение без раскрытия алгоритма хеширования.


Контроллер авторизации

const authService = require('../services/authService');

async function login(ctx) {
    const { email, password } = ctx.request.body;

    const user = await authService.login(email, password);

    if (!user) {
        ctx.status = 401;
        ctx.body = { message: 'Invalid credentials' };
        return;
    }

    ctx.status = 200;
    ctx.body = {
        id: user._id,
        email: user.email
    };
}

module.exports = {
    login
};

Подключение маршрутов Koa

Маршрутизация в Koa обычно реализуется через koa-router:

const Router = require('koa-router');
const authController = require('../controllers/authController');

const router = new Router();

router.post('/register', authController.register);
router.post('/login', authController.login);

module.exports = router;

Подключение в основном приложении:

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const authRoutes = require('./routes/authRoutes');

const app = new Koa();

app.use(bodyParser());
app.use(authRoutes.routes());

app.listen(3000);

Обработка ошибок через middleware

Koa позволяет централизованно обрабатывать ошибки, возникающие в процессе работы с паролями и пользователями.

app.use(async (ctx, next) => {
    try {
        await next();
    } catch (err) {
        ctx.status = err.status || 500;
        ctx.body = {
            message: err.message
        };
    }
});

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


Работа с async/await и асинхронностью

Вся цепочка обработки запросов в Koa основана на промисах. Операции с базой данных и хешированием выполняются асинхронно, что позволяет не блокировать event loop.

Важно, что password-hash работает синхронно, поэтому при высокой нагрузке может потребоваться вынос в отдельный поток или замена на более современный алгоритм (например, bcrypt или argon2).


Хранение паролей в базе данных

В модели пользователя поле пароля хранит только хеш:

const mongoose = require('mongoose');

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

module.exports = mongoose.model('User', UserSchema);

Структура не содержит никаких дополнительных полей для соли или параметров алгоритма, так как они инкапсулированы внутри строки хеша.


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

Процесс смены пароля повторяет регистрацию:

async function changePassword(userId, newPassword) {
    const hashedPassword = passwordHash.generate(newPassword);

    await User.updateOne(
        { _id: userId },
        { password: hashedPassword }
    );
}

Безопасность и ограничения password-hash

Несмотря на удобство, библиотека имеет ограничения:

  • синхронное выполнение операций;
  • меньшая устойчивость к специализированным атакам по сравнению с bcrypt;
  • устаревшие алгоритмы в некоторых конфигурациях.

Для критичных систем предпочтение обычно отдается адаптивным алгоритмам:

  • bcrypt
  • argon2
  • scrypt

Сервисный слой авторизации как единая точка контроля

Вынесение логики работы с паролями в отдельный сервис обеспечивает:

  • централизованное управление алгоритмом хеширования;
  • упрощение тестирования;
  • изоляцию бизнес-логики от HTTP-слоя.
class AuthService {
    async hashPassword(password) {
        return passwordHash.generate(password);
    }

    async verifyPassword(password, hash) {
        return passwordHash.verify(password, hash);
    }
}

module.exports = new AuthService();

Применение в реальных Koa-приложениях

В типичной архитектуре Koa использование password-hash ограничивается следующими зонами:

  • регистрация пользователей;
  • проверка учетных данных;
  • миграция старых паролей;
  • административное управление пользователями.

Контроллеры остаются тонкими, а вся логика безопасности концентрируется в сервисах, что упрощает масштабирование системы и замену алгоритма хеширования без переписывания маршрутов.