Библиотека 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
};
}
}
Все операции используют единый набор параметров, что гарантирует консистентность хешей в разных частях приложения.
Архитектурно важно отделить:
Обёртка выполняет роль инфраструктурного адаптера. Бизнес-слой взаимодействует только с абстракцией:
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-hashhash, verify,
needsRehashТакой слой становится стандартной точкой взаимодействия с паролями в системе и заменяет прямое использование сторонней библиотеки во всех сервисах.