Библиотека password-hash представляет собой
высокоуровневую абстракцию для хранения паролей. Модуль
crypto в Node.js — низкоуровневый криптографический
инструмент общего назначения.
Главное различие заключается в уровне ответственности:
| Подход | Назначение |
|---|---|
password-hash |
Готовое решение для безопасного хранения паролей |
crypto |
Набор криптографических примитивов |
При использовании password-hash разработчик работает с
API, ориентированным именно на пароли:
const passwordHash = require('password-hash');
const hash = passwordHash.generate('secret-password');
const isValid = passwordHash.verify(
'secret-password',
hash
);
При использовании crypto необходимо самостоятельно
проектировать:
Пример через crypto:
const crypto = require('crypto');
function hashPassword(password) {
const salt = crypto.randomBytes(32).toString('hex');
const hash = crypto
.pbkdf2Sync(password, salt, 100000, 64, 'sha512')
.toString('hex');
return `${salt}:${hash}`;
}
Разница в объёме инфраструктурного кода становится особенно заметной в крупных проектах.
password-hashБиблиотека автоматизирует несколько критически важных задач:
Результат выглядит примерно так:
sha1$3f6a9f$1$8f7d...
Внутри строки уже содержатся:
Разработчику не требуется самостоятельно проектировать формат хранения.
cryptoБольшинство уязвимостей возникает не из-за плохой криптографии, а из-за неправильного использования примитивов.
Типичные ошибки:
Неправильно:
const hash = crypto
.createHash('sha256')
.update(password)
.digest('hex');
Причины небезопасности:
const salt = 'static-salt';
Это делает все пароли предсказуемыми.
crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512');
Современные значения обычно начинаются от десятков или сотен тысяч итераций.
{
passwordHash: '...',
salt: '...'
}
Подобная схема быстро превращается в источник несовместимостей между сервисами.
password-hash безопаснее по умолчаниюВысокоуровневая библиотека минимизирует вероятность критических ошибок.
const hash = passwordHash.generate(password);
Соль создаётся автоматически через криптографически стойкий генератор случайных чисел.
Строка уже содержит:
Не требуется создавать собственный формат.
passwordHash.verify(password, hash);
Разработчик не сравнивает хеши вручную.
cryptoМодуль Node.js предоставляет доступ к:
Это мощный фундаментальный инструмент.
Пример генерации случайной соли:
const salt = crypto.randomBytes(16);
Пример PBKDF2:
crypto.pbkdf2Sync(
password,
salt,
100000,
64,
'sha512'
);
Пример HMAC:
crypto.createHmac('sha256', key);
Однако модуль не знает, что именно хранится — пароль, токен, ключ или произвольные данные.
Поэтому безопасность полностью зависит от архитектуры приложения.
password-hashМинимальный код:
const hash = passwordHash.generate(password);
Проверка:
passwordHash.verify(password, hash);
Подходит для:
cryptoТребуется самостоятельно реализовать:
Пример полноценной схемы:
const crypto = require('crypto');
function hashPassword(password) {
const iterations = 120000;
const keyLength = 64;
const digest = 'sha512';
const salt = crypto
.randomBytes(32)
.toString('hex');
const hash = crypto
.pbkdf2Sync(
password,
salt,
iterations,
keyLength,
digest
)
.toString('hex');
return [
digest,
iterations,
salt,
hash
].join('$');
}
cryptoНесмотря на сложность, низкоуровневый подход обладает огромным преимуществом — полной управляемостью.
Можно:
password-hash становится ограничениемВысокоуровневая библиотека подходит не всегда.
Нельзя гибко управлять:
Если библиотека перестаёт поддерживаться:
Современные системы чаще используют:
Некоторые старые библиотеки ориентированы на SHA1/PBKDF2 и уже не соответствуют современным требованиям.
crypto.scryptNode.js поддерживает scrypt напрямую.
Пример:
const crypto = require('crypto');
function hashPassword(password) {
const salt = crypto.randomBytes(16).toString('hex');
const hash = crypto
.scryptSync(password, salt, 64)
.toString('hex');
return `${salt}:${hash}`;
}
scrypt существенно устойчивее к GPU-атакам по сравнению
с SHA256.
При прямом использовании crypto разработчики часто
создают собственные схемы хранения:
salt:hash
или:
hash:salt:iterations
или даже:
{
"salt": "...",
"hash": "...",
"algo": "sha512"
}
Со временем появляются проблемы:
password-hash стандартизирует эти процессы.
password-hashconst hash = passwordHash.generate(password);
const ok = passwordHash.verify(
password,
hash
);
cryptoconst crypto = require('crypto');
function createHash(password) {
const salt = crypto.randomBytes(16);
const hash = crypto.pbkdf2Sync(
password,
salt,
120000,
64,
'sha512'
);
return {
salt: salt.toString('hex'),
hash: hash.toString('hex')
};
}
function verify(password, stored) {
const hash = crypto.pbkdf2Sync(
password,
Buffer.from(stored.salt, 'hex'),
120000,
64,
'sha512'
);
return hash.toString('hex') === stored.hash;
}
password-hashНастройки ограничены API библиотеки.
Например:
passwordHash.generate(password, {
algorithm: 'sha512',
saltLength: 32,
iterations: 15000
});
Но доступный набор возможностей зависит от реализации.
cryptoПолный контроль:
crypto.pbkdf2Sync(
password,
salt,
310000,
128,
'sha512'
);
Можно изменять:
password-hashДобавляет:
Накладные расходы обычно незначительны.
cryptoРаботает ближе к системному уровню.
Преимущества:
Особенно важно для:
password-hashЧасть логики скрыта внутри библиотеки.
Иногда сложно понять:
cryptoВесь процесс полностью прозрачен:
const salt = crypto.randomBytes(32);
const hash = crypto.pbkdf2Sync(
password,
salt,
200000,
64,
'sha512'
);
Каждый этап контролируется напрямую.
password-hashcryptoОбычное сравнение строк:
hash1 === hash2
может быть уязвимо к timing attack.
В crypto существует безопасное сравнение:
crypto.timingSafeEqual(
Buffer.from(hash1),
Buffer.from(hash2)
);
Высокоуровневые библиотеки обычно скрывают этот механизм внутри себя.
password-hashПодходит для простых схем:
password → hash → verify
cryptoПозволяет строить сложные пайплайны:
password
↓
pre-hash normalization
↓
pepper
↓
salt
↓
PBKDF2/scrypt
↓
custom encoding
↓
versioned storage
Подобные архитектуры часто используются в крупных системах.
При использовании crypto можно хранить версии схем:
v1$sha256$...
v2$pbkdf2$...
v3$scrypt$...
Это позволяет:
У password-hash подобные возможности обычно ограничены
внутренней реализацией библиотеки.
password-hashБиблиотека берёт ответственность на себя:
Это уменьшает число ошибок.
cryptoОтветственность полностью лежит на разработчике:
Взамен предоставляется максимальная гибкость.