При построении серверных приложений на Node.js одной из ключевых
задач является безопасное хранение паролей пользователей. Использование
библиотеки password-hash в связке с PostgreSQL позволяет
организовать простую и надёжную схему аутентификации, не прибегая к
самостоятельной реализации криптографических алгоритмов.
Основной принцип заключается в том, что в базе данных хранится не сам пароль, а его криптографический хеш. При проверке входа выполняется сравнение введённого значения с сохранённым хешем через функцию проверки библиотеки.
Библиотека password-hash предоставляет простой API для
создания и проверки хешей. При регистрации пользователя пароль
преобразуется в защищённую строку:
const passwordHash = require('password-hash');
const plainPassword = 'user_password_123';
const hashedPassword = passwordHash.generate(plainPassword);
Полученный hashedPassword уже содержит информацию о соли
и алгоритме, используемом для генерации.
Ключевые особенности формата хеша:
Для хранения данных пользователей используется стандартная таблица, где пароль представлен как текстовое поле:
CRE ATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
Поле password_hash предназначено исключительно для
хранения результата функции password-hash.generate.
Низкоуровневая интеграция выполняется через пакет pg. Он
обеспечивает прямое взаимодействие с PostgreSQL без ORM.
const { Pool } = require('pg');
const pool = new Pool({
user: 'db_user',
host: 'localhost',
database: 'app_db',
password: 'secure_password',
port: 5432,
});
const passwordHash = require('password-hash');
async function createUser(email, password) {
const hash = passwordHash.generate(password);
const query = `
INS ERT IN TO users (email, password_hash)
VALUES ($1, $2)
RETURNING id, email
`;
const values = [email, hash];
const result = await pool.query(query, values);
return result.rows[0];
}
При входе пользователя выполняется выборка записи и проверка пароля
через password-hash.verify.
async function authenticateUser(email, password) {
const query = `SEL ECT * FR OM users WHERE email = $1`;
const result = await pool.query(query, [email]);
if (result.rows.length === 0) {
return null;
}
const user = result.rows[0];
const isValid = passwordHash.verify(password, user.password_hash);
if (!isValid) {
return null;
}
return user;
}
Механизм проверки полностью скрывает детали хеширования, так как библиотека извлекает соль и параметры алгоритма из сохранённой строки.
ORM Sequelize упрощает работу с моделями и миграциями, сохраняя совместимость с PostgreSQL и хешированием паролей.
const { Model, DataTypes } = require('sequelize');
class User extends Model {}
User.init({
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
password_hash: {
type: DataTypes.TEXT,
allowNull: false,
}
}, {
sequelize,
modelName: 'User',
tableName: 'users',
timestamps: true
});
Sequelize позволяет автоматизировать процесс хеширования с помощью hooks:
const passwordHash = require('password-hash');
User.beforeCreate((user) => {
user.password_hash = passwordHash.generate(user.password_hash);
});
При создании записи в модель перед сохранением пароль автоматически преобразуется в хеш.
Логика аутентификации может быть вынесена в метод модели:
User.prototype.validatePassword = function(password) {
return passwordHash.verify(password, this.password_hash);
};
Использование:
const user = await User.findOne({ where: { email } });
if (!user || !user.validatePassword(password)) {
return null;
}
При изменении пароля необходимо повторно выполнять хеширование:
User.beforeUpdate((user) => {
if (user.changed('password_hash')) {
user.password_hash = passwordHash.generate(user.password_hash);
}
});
Это предотвращает сохранение открытого текста при обновлении записи.
При работе с password-hash и PostgreSQL важно учитывать
ряд практических аспектов:
TEXT;verify, а не через
сравнение строк.При использовании миграций структура таблицы фиксируется отдельно от логики приложения:
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password_hash: {
type: Sequelize.TEXT,
allowNull: false
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
}
};
При росте нагрузки архитектура с PostgreSQL и Sequelize остаётся устойчивой при соблюдении следующих принципов:
email для ускорения поиска;pg;Допустимо одновременное использование низкоуровневого pg
и ORM Sequelize:
pg применяется для сложных или оптимизированных
SQL-запросов;При этом хеширование паролей остаётся независимым слоем и одинаково работает в обеих схемах интеграции.
В практических реализациях часто встречаются следующие проблемы:
=== вместо verify;Такие ошибки приводят к нарушению безопасности и некорректной аутентификации.
Генерация и проверка хеша являются CPU-зависимыми операциями. При массовой регистрации или авторизации следует учитывать: