Хранение паролей в исходном виде недопустимо даже в небольших учебных проектах. Любая утечка базы данных в таком случае приводит к компрометации всех пользователей. Хэширование решает эту проблему за счёт необратимого преобразования пароля в строку фиксированной длины.
Библиотека password-hash реализует простой механизм:
Ключевой принцип: в базе хранится только результат хэширования, а не сам пароль.
Типичная структура Express-проекта с авторизацией включает:
Пример установки:
npm install express password-hash
Инициализация приложения:
const express = require('express');
const passwordHash = require('password-hash');
const app = express();
app.use(express.json());
Регистрация — это процесс создания нового пользователя и сохранения его пароля в виде хэша.
Основная последовательность действий:
Пример реализации:
const users = [];
app.post('/register', (req, res) => {
const { username, password } = req.body;
const existingUser = users.find(u => u.username === username);
if (existingUser) {
return res.status(400).json({ message: 'Пользователь уже существует' });
}
const hashedPassword = passwordHash.generate(password);
const newUser = {
id: Date.now(),
username,
password: hashedPassword
};
users.push(newUser);
res.status(201).json({ message: 'Пользователь создан' });
});
При генерации хэша password-hash автоматически добавляет
соль. Это означает:
Пример результата:
sha1$5b1c2d$3f8a1e9c...
Структура включает алгоритм, соль и сам хэш.
Авторизация основана на сравнении введённого пароля с сохранённым хэшем.
Ключевая ошибка новичков — попытка сравнивать строки напрямую. Это невозможно, так как пароль не хранится в открытом виде.
Правильный подход:
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(400).json({ message: 'Пользователь не найден' });
}
const isValid = passwordHash.verify(password, user.password);
if (!isValid) {
return res.status(401).json({ message: 'Неверный пароль' });
}
res.json({ message: 'Успешный вход' });
});
Функция verify выполняет несколько операций:
Важно, что сравнение происходит безопасным способом, предотвращающим утечки через timing attacks в базовой реализации.
Даже в небольшом приложении рекомендуется разделять логику:
Пример сервиса пользователей:
const passwordHash = require('password-hash');
class UserService {
constructor() {
this.users = [];
}
createUser(username, password) {
const hashedPassword = passwordHash.generate(password);
const user = {
id: Date.now(),
username,
password: hashedPassword
};
this.users.push(user);
return user;
}
authenticate(username, password) {
const user = this.users.find(u => u.username === username);
if (!user) return null;
const isValid = passwordHash.verify(password, user.password);
if (!isValid) return null;
return user;
}
}
module.exports = new UserService();
const express = require('express');
const userService = require('./userService');
const router = express.Router();
router.post('/register', (req, res) => {
const { username, password } = req.body;
const user = userService.createUser(username, password);
res.status(201).json({ id: user.id, username: user.username });
});
router.post('/login', (req, res) => {
const { username, password } = req.body;
const user = userService.authenticate(username, password);
if (!user) {
return res.status(401).json({ message: 'Ошибка авторизации' });
}
res.json({ message: 'Успешный вход', user: user.username });
});
module.exports = router;
При работе с хэшированием часто встречаются ошибки:
1. Повторное хэширование уже захэшированного пароля
2. Сравнение строк вместо verify
3. Отсутствие проверки существования пользователя
Хотя библиотека упрощает работу, она не относится к современным криптографическим стандартам уровня bcrypt или argon2. В учебных проектах этого достаточно, но важно учитывать:
В реальных системах чаще применяются:
Регистрация:
клиент → Express → generate(password) → хэш → хранение
Логин:
клиент → Express → verify(password, hash) → результат → ответ
Этот поток обеспечивает разделение между исходными данными пользователя и тем, что хранится в системе.
Проверка авторизации может быть вынесена в middleware:
function authMiddleware(req, res, next) {
const { username, password } = req.headers;
const user = userService.authenticate(username, password);
if (!user) {
return res.status(401).json({ message: 'Доступ запрещён' });
}
req.user = user;
next();
}
Использование:
app.get('/profile', authMiddleware, (req, res) => {
res.json({ user: req.user.username });
});
При переходе от учебного проекта к полноценной системе структура авторизации обычно расширяется:
password-hash в таких системах может быть заменён, но
логика разделения остаётся идентичной: регистрация → хэширование →
хранение, логин → проверка → выдача сессии.
Express приложение с password-hash обычно строится по следующей схеме:
Такое разделение позволяет сохранять предсказуемость поведения системы и упрощает замену компонентов без изменения маршрутов.