Работа с паролями в серверных приложениях на Node.js требует строгого соблюдения принципов безопасности: хранение только хешей, использование соли, контроль алгоритма и защита от перебора. Библиотека password-hash предоставляет простой интерфейс для создания и проверки хешей, а Fastify позволяет встроить этот процесс в высокопроизводительный HTTP-сервер.
Установка необходимых пакетов:
npm install fastify password-hash
Fastify используется как основа API, а password-hash — как слой обработки паролей.
Библиотека реализует классическую схему:
Пример использования без Fastify:
const passwordHash = require('password-hash');
const hashedPassword = passwordHash.generate('mySecurePassword');
const isValid = passwordHash.verify('mySecurePassword', hashedPassword);
Строка хеша содержит алгоритм, соль и сам хеш, что упрощает хранение в базе данных.
В архитектуре Fastify логично вынести работу с паролями в отдельный модуль, чтобы не смешивать бизнес-логику и обработчики маршрутов.
// services/passwordService.js
const passwordHash = require('password-hash');
function hashPassword(password) {
return passwordHash.generate(password);
}
function verifyPassword(password, hashed) {
return passwordHash.verify(password, hashed);
}
module.exports = {
hashPassword,
verifyPassword
};
Такой слой упрощает тестирование и дальнейшую замену алгоритма.
При создании пользователя пароль никогда не сохраняется в открытом виде.
const fastify = require('fastify')({ logger: true });
const { hashPassword } = require('./services/passwordService');
fastify.post('/register', async (request, reply) => {
const { email, password } = request.body;
const hashed = hashPassword(password);
const user = {
email,
password: hashed
};
// здесь обычно идет запись в БД
return reply.code(201).send(user);
});
Ключевой момент: в базу данных попадает только результат хеширования.
При логине выполняется сравнение введённого пароля с сохранённым хешем.
const { verifyPassword } = require('./services/passwordService');
fastify.post('/login', async (request, reply) => {
const { email, password } = request.body;
// имитация поиска пользователя в базе
const userFromDb = {
email,
password: '$hashed_password_from_db'
};
const isValid = verifyPassword(password, userFromDb.password);
if (!isValid) {
return reply.code(401).send({ error: 'Invalid credentials' });
}
return reply.send({ status: 'ok' });
});
Проверка выполняется без раскрытия исходного пароля.
Fastify позволяет внедрять хуки, которые полезны при построении систем аутентификации.
fastify.addHook('preHandler', async (request, reply) => {
if (!request.headers.authorization) {
return reply.code(401).send({ error: 'No token' });
}
});
Хотя password-hash не работает с токенами напрямую, он часто используется в связке с JWT или сессионной моделью.
Хеш, создаваемый password-hash, уже содержит всю необходимую информацию:
Поэтому структура таблицы пользователей минимальна:
CRE ATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
Поле password хранит только результат passwordHash.generate().
При интеграции важно учитывать:
Пример валидации Fastify:
fastify.post('/register', {
schema: {
body: {
type: 'object',
required: ['email', 'password'],
properties: {
email: { type: 'string' },
password: { type: 'string', minLength: 6 }
}
}
}
}, async (request, reply) => {
const { email, password } = request.body;
const hashed = hashPassword(password);
return { email, password: hashed };
});
Встроенная схема снижает риск попадания некорректных данных в слой хеширования.
Fastify оптимизирован под высокую пропускную способность, однако password-hash выполняет синхронное хеширование, что может стать узким местом при большом количестве запросов.
Типичные меры:
Пример ограничения:
fastify.register(require('@fastify/rate-limit'), {
max: 100,
timeWindow: '1 minute'
});
На практике password-hash часто используется только на этапе логина и регистрации, а дальше применяется JWT.
const jwt = require('jsonwebtoken');
function createToken(user) {
return jwt.sign({ id: user.id }, 'secretKey');
}
После проверки пароля генерируется токен, а password-hash больше не участвует в запросах.
Часто встречающиеся проблемы:
Особенно критична ошибка двойного хеширования:
const wrong = passwordHash.generate(passwordHash.generate('test'));
Это приводит к невозможности проверки пароля.
Рекомендуемая структура:
/plugins
/services
/routes
/controllers
Password-hash обычно находится в
/services/authService.js, а маршруты лишь вызывают
методы:
// controllers/authController.js
const { hashPassword } = require('../services/passwordService');
Такой подход снижает связанность компонентов и упрощает тестирование логики безопасности.