Обработка паролей в серверных приложениях требует строгого соблюдения принципов одностороннего преобразования данных. Библиотека password-hash реализует механизм хеширования с добавлением соли, позволяя безопасно хранить пароли в базе данных и проверять их без обратного восстановления исходного значения.
В экосистеме Node.js подобные библиотеки используются на уровне бизнес-логики, а в связке с Koa интегрируются через сервисный слой и middleware, обеспечивая разделение ответственности между маршрутизацией, обработкой запроса и безопасностью данных.
Базовая установка выполняется через npm:
npm install password-hash
Подключение в модуле сервиса:
const passwordHash = require('password-hash');
Библиотека предоставляет два ключевых метода:
Процесс генерации хеша включает:
Хранимое значение включает не только результат хеширования, но и параметры, необходимые для последующей проверки. Это исключает необходимость отдельного хранения соли.
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, а исходный пароль не сохраняется нигде в системе.
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-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);
Koa позволяет централизованно обрабатывать ошибки, возникающие в процессе работы с паролями и пользователями.
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {
message: err.message
};
}
});
Такой подход исключает необходимость дублирования обработки ошибок в каждом контроллере.
Вся цепочка обработки запросов в 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 }
);
}
Несмотря на удобство, библиотека имеет ограничения:
Для критичных систем предпочтение обычно отдается адаптивным алгоритмам:
Вынесение логики работы с паролями в отдельный сервис обеспечивает:
class AuthService {
async hashPassword(password) {
return passwordHash.generate(password);
}
async verifyPassword(password, hash) {
return passwordHash.verify(password, hash);
}
}
module.exports = new AuthService();
В типичной архитектуре Koa использование password-hash ограничивается следующими зонами:
Контроллеры остаются тонкими, а вся логика безопасности концентрируется в сервисах, что упрощает масштабирование системы и замену алгоритма хеширования без переписывания маршрутов.