Написание обёртки над password-hash с единым интерфейсом

Библиотека password-hash в JavaScript предоставляет базовые операции хеширования паролей и проверки их соответствия, однако её прямое использование в прикладной архитектуре приводит к тесной связанности кода с конкретной реализацией алгоритма и формата хеша. Для построения устойчивой системы аутентификации целесообразно вводить слой абстракции, который унифицирует работу с паролями и скрывает детали реализации.

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


Инкапсуляция операций хеширования

Базовый набор операций, который требуется от любого механизма хеширования паролей, включает:

  • генерацию хеша из исходного пароля
  • проверку соответствия пароля и хеша
  • определение необходимости повторного хеширования при изменении параметров

При использовании password-hash эти операции уже существуют, но их сигнатуры и формат данных не всегда удобно использовать напрямую в прикладном коде. Обёртка вводит стабильный интерфейс:

class PasswordHasher {
    hash(password) {}
    verify(password, hashed) {}
    needsRehash(hashed) {}
}

Такой контракт позволяет не привязываться к внутреннему API конкретной библиотеки.


Приведение внешней библиотеки к доменной модели

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

Пример реализации:

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

class PasswordHashAdapter {
    hash(password, options = {}) {
        return passwordHash.generate(password, {
            algorithm: options.algorithm || 'sha1',
            iterations: options.iterations || 10
        });
    }

    verify(password, hashed) {
        return passwordHash.verify(password, hashed);
    }

    needsRehash(hashed, options = {}) {
        const parsed = passwordHash.isHashed(hashed);

        if (!parsed) {
            return true;
        }

        if (options.algorithm && parsed.algorithm !== options.algorithm) {
            return true;
        }

        if (options.iterations && parsed.iterations !== options.iterations) {
            return true;
        }

        return false;
    }
}

Здесь внешняя библиотека полностью изолирована, а логика проверки параметров вынесена в слой адаптера.


Нормализация параметров хеширования

В реальных системах параметры хеширования часто изменяются: увеличивается количество итераций, меняется алгоритм, вводится соль. Без централизованной точки управления это приводит к фрагментации логики.

Обёртка вводит конфигурационный слой:

class PasswordHasher {
    constructor(config) {
        this.config = {
            algorithm: 'sha256',
            iterations: 12,
            ...config
        };
    }
}

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


Разделение ответственности

Архитектурно важно отделить:

  • доменную логику (регистрация, авторизация)
  • инфраструктурный слой (хеширование)
  • внешние зависимости (password-hash)

Обёртка выполняет роль инфраструктурного адаптера. Бизнес-слой взаимодействует только с абстракцией:

class AuthService {
    constructor(passwordHasher) {
        this.passwordHasher = passwordHasher;
    }

    register(password) {
        const hash = this.passwordHasher.hash(password);
        return { passwordHash: hash };
    }

    login(password, storedHash) {
        return this.passwordHasher.verify(password, storedHash);
    }
}

Такой подход исключает прямую зависимость от конкретной библиотеки хеширования.


Обработка ошибок и некорректных данных

Низкоуровневые библиотеки часто возвращают неоднородные результаты: true/false, строки ошибок или исключения. Обёртка нормализует поведение, приводя его к предсказуемой модели.

class PasswordHashAdapter {
    verify(password, hashed) {
        try {
            return passwordHash.verify(password, hashed);
        } catch (e) {
            return false;
        }
    }
}

Это предотвращает утечку исключений в бизнес-слой и упрощает обработку ошибок на уровне сервисов.


Поддержка миграции алгоритмов

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

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

needsRehash(hashed) {
    const meta = passwordHash.isHashed(hashed);

    if (!meta) {
        return true;
    }

    return meta.iterations < this.config.iterations;
}

Таким образом реализуется стратегия постепенного обновления паролей при следующем входе пользователя.


Унификация формата хеша

Разные версии библиотек могут возвращать хеши в различных форматах. Обёртка фиксирует единый контракт хранения:

  • строковое представление
  • включение метаданных (алгоритм, соль, итерации)
  • обратимая структура для проверки

Это позволяет сохранять совместимость при смене реализации.


Тестируемость и подмена реализации

Наличие обёртки упрощает тестирование системы. Вместо реального хеширования можно использовать заглушку:

class MockPasswordHasher {
    hash(password) {
        return `hashed:${password}`;
    }

    verify(password, hashed) {
        return hashed === `hashed:${password}`;
    }

    needsRehash() {
        return false;
    }
}

Это изолирует тесты бизнес-логики от криптографических операций и ускоряет выполнение тестового набора.


Расширяемость интерфейса

Единый интерфейс позволяет добавлять дополнительные возможности без изменения существующего кода:

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

Каждое расширение реализуется внутри обёртки, не затрагивая потребителей интерфейса.


Контроль безопасности на уровне абстракции

Инкапсуляция хеширования в отдельный слой позволяет централизованно управлять критичными параметрами безопасности:

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

Это исключает ситуацию, когда разные части системы используют разные параметры, что часто приводит к уязвимостям или невозможности аутентификации.


Структура типовой реализации

Обобщённая структура обёртки включает:

  • класс-адаптер для password-hash
  • конфигурационный слой
  • методы hash, verify, needsRehash
  • обработку ошибок
  • нормализацию форматов
  • поддержку миграции

Такой слой становится стандартной точкой взаимодействия с паролями в системе и заменяет прямое использование сторонней библиотеки во всех сервисах.