В криптографической защите паролей помимо хэширования и соли применяется дополнительный секретный компонент — перец (pepper). В отличие от соли, которая хранится вместе с хэшем и уникальна для каждого пользователя, перец представляет собой глобальный секрет приложения, не сохраняемый в базе данных.
Ключевые свойства перца:
Даже при компрометации базы данных злоумышленник не получает полного набора данных для восстановления паролей, так как отсутствует дополнительный секретный элемент.
Библиотека password-hash в JavaScript реализует
классическую схему:
Типичный интерфейс:
const passwordHash = require('password-hash');
const hashed = passwordHash.generate('myPassword123');
const verified = passwordHash.verify('myPassword123', hashed);
Внутри библиотека оперирует солью, но не предусматривает механизмов глобального секрета.
Отсутствие встроенного перца создаёт архитектурный разрыв между криптографической моделью и требованиями безопасности современных систем.
Основные проблемы:
Даже при использовании сильных алгоритмов хэширования добавление перца повышает устойчивость системы к атакам перебора.
Подход с обёрткой позволяет внедрить поддержку перца без изменения исходной библиотеки.
Общая идея:
password-hashСтруктура:
generateverifyПерец не должен находиться в коде напрямую.
const PEPPER = process.env.PASSWORD_PEPPER;
В продакшн-средах переменная задаётся через окружение, секретные хранилища или инфраструктурные решения.
const passwordHash = require('password-hash');
const crypto = require('crypto');
function applyPepper(password, pepper) {
return crypto
.createHmac('sha256', pepper)
.update(password)
.digest('hex');
}
function generateHash(password) {
const peppered = applyPepper(password, PEPPER);
return passwordHash.generate(peppered);
}
Здесь выполняется предварительное преобразование пароля через HMAC, что предотвращает прямое комбинирование строк и снижает риск утечек промежуточных значений.
function verifyPassword(password, storedHash) {
const peppered = applyPepper(password, PEPPER);
return passwordHash.verify(peppered, storedHash);
}
Проверка выполняется симметрично: входной пароль проходит тот же этап преобразования, после чего сравнивается с сохранённым хэшем.
В архитектуре приложения обёртка выступает заменой прямого обращения к библиотеке:
// вместо passwordHash.generate / verify
generateHash(password);
verifyPassword(password, hash);
Это позволяет:
Дополнительная защита может быть реализована через комбинирование нескольких секретов:
function applyPepper(password, pepper) {
return crypto
.createHmac('sha512', pepper)
.update(password)
.digest('base64');
}
Разные окружения используют разные секреты:
Это снижает риск переноса атак между средами.
Любое включение перца в исходный код приводит к полной компрометации схемы защиты при утечке репозитория.
Различие между алгоритмами в генерации и проверке приводит к невозможности валидации паролей.
password + pepper
Такой подход упрощает предсказание структуры входных данных и снижает криптостойкость по сравнению с HMAC.
Добавление перца через обёртку формирует дополнительный уровень защиты, который:
password-hashАрхитектурно такой подход разделяет ответственность: