Добавление поддержки перца через обёртку

Смысл перца в схеме хранения паролей

В криптографической защите паролей помимо хэширования и соли применяется дополнительный секретный компонент — перец (pepper). В отличие от соли, которая хранится вместе с хэшем и уникальна для каждого пользователя, перец представляет собой глобальный секрет приложения, не сохраняемый в базе данных.

Ключевые свойства перца:

  • Единый секрет для всей системы
  • Хранится вне базы данных (в переменных окружения или HSM)
  • Добавляется к паролю до выполнения хэширования
  • Снижает эффективность атак при утечке базы данных

Даже при компрометации базы данных злоумышленник не получает полного набора данных для восстановления паролей, так как отсутствует дополнительный секретный элемент.


Базовая модель работы password-hash

Библиотека password-hash в JavaScript реализует классическую схему:

  1. Генерация соли
  2. Объединение пароля и соли
  3. Вычисление хэша
  4. Сохранение результата в формате, содержащем алгоритм, соль и хэш

Типичный интерфейс:

const passwordHash = require('password-hash');

const hashed = passwordHash.generate('myPassword123');
const verified = passwordHash.verify('myPassword123', hashed);

Внутри библиотека оперирует солью, но не предусматривает механизмов глобального секрета.


Ограничения отсутствия поддержки перца

Отсутствие встроенного перца создаёт архитектурный разрыв между криптографической моделью и требованиями безопасности современных систем.

Основные проблемы:

  • утечка базы данных позволяет проводить офлайн-атаки
  • одинаковые пароли с разными пользователями могут анализироваться статистически
  • отсутствует дополнительный слой защиты при компрометации хэшей

Даже при использовании сильных алгоритмов хэширования добавление перца повышает устойчивость системы к атакам перебора.


Архитектура обёртки для добавления перца

Подход с обёрткой позволяет внедрить поддержку перца без изменения исходной библиотеки.

Общая идея:

  1. Создаётся отдельный секретный ключ (pepper)
  2. Пароль модифицируется перед передачей в password-hash
  3. Все операции генерации и проверки проходят через единый слой

Структура:

  • модуль конфигурации (хранение перца)
  • обёртка над generate
  • обёртка над verify

Реализация обёртки

Хранение перца

Перец не должен находиться в коде напрямую.

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');
}

Разделение перца по средам

Разные окружения используют разные секреты:

  • development
  • staging
  • production

Это снижает риск переноса атак между средами.


Типичные ошибки реализации

Хранение перца в коде

Любое включение перца в исходный код приводит к полной компрометации схемы защиты при утечке репозитория.

Несогласованность операций

Различие между алгоритмами в генерации и проверке приводит к невозможности валидации паролей.

Использование простого конкатенирования

password + pepper

Такой подход упрощает предсказание структуры входных данных и снижает криптостойкость по сравнению с HMAC.


Влияние обёртки на безопасность системы

Добавление перца через обёртку формирует дополнительный уровень защиты, который:

  • не зависит от выбранного алгоритма хэширования
  • сохраняет обратную совместимость с библиотекой password-hash
  • усложняет офлайн-подбор даже при наличии утечки базы данных

Архитектурно такой подход разделяет ответственность:

  • библиотека отвечает за хэширование
  • обёртка отвечает за усиление входных данных
  • инфраструктура отвечает за хранение секрета