Совместимость хешей при смене параметров

Хеши паролей обладают важным свойством: при одинаковых входных данных и одинаковых параметрах алгоритма результат должен быть воспроизводим. Именно это свойство делает возможной проверку пароля без его хранения в открытом виде. Однако при изменении параметров хеширования возникает проблема совместимости: старые хеши продолжают существовать в базе, но новые уже формируются иначе.

В библиотеке Password-hash (как и в большинстве современных решений для хеширования паролей) результат зависит от набора параметров, которые могут меняться со временем:

  • алгоритм хеширования (например, SHA-256, bcrypt, Argon2)
  • соль (salt) и её длина
  • количество итераций (iterations)
  • фактор стоимости (cost factor / work factor)
  • параметры памяти (memory cost, parallelism)
  • формат кодирования результата

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

Проблема неизменяемости уже сохранённых хешей

Сохранённые в базе хеши являются «снимком» конкретной конфигурации алгоритма. При изменении параметров возникает ситуация:

  • старые хеши нельзя пересчитать, так как исходные пароли неизвестны
  • новые хеши нельзя сравнивать напрямую со старыми
  • единая схема проверки перестаёт существовать

По этой причине любые изменения в параметрах требуют стратегии миграции, а не простого обновления конфигурации.

Версионирование параметров хеширования

Одним из базовых решений является включение версии схемы в сам хеш. В Password-hash это часто реализуется через структурированный формат строки:

algorithm:version:parameters:hash

Пример:

pbkdf2:v1:iterations=10000:salt.hash
pbkdf2:v2:iterations=200000:salt.hash

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

Проверка пароля с учётом версии

При аутентификации процесс проверки обычно включает следующие шаги:

  1. Извлечение версии из сохранённого хеша
  2. Выбор соответствующего алгоритма и параметров
  3. Вычисление хеша от введённого пароля
  4. Сравнение результатов в защищённом режиме

Пример логики на Jav * aScript:

import passwordHash from "password-hash";

function verifyPassword(password, storedHash) {
  const parsed = passwordHash.parse(storedHash);

  if (parsed.algo === "pbkdf2" && parsed.version === 1) {
    return passwordHash.pbkdf2.verify(password, storedHash);
  }

  if (parsed.algo === "pbkdf2" && parsed.version === 2) {
    return passwordHash.pbkdf2.verify(password, storedHash);
  }

  return false;
}

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

Проблема изменения параметров без версии

Если библиотека не поддерживает версионирование, возникает скрытая несовместимость. Например:

  • ранее использовалось 10 000 итераций
  • затем параметр увеличен до 200 000

В этом случае новые хеши невозможно отличить от старых, что приводит к двум рискам:

  • снижение безопасности старых записей без явного контроля
  • невозможность корректной миграции

Механизм постепенной миграции (rehashing)

Практическая стратегия обновления хешей заключается в «перехешировании при входе»:

  1. пользователь вводит пароль
  2. пароль проверяется по старому алгоритму
  3. при успешной проверке создаётся новый хеш с актуальными параметрами
  4. новый хеш сохраняется в базе вместо старого

Пример:

function login(password, user) {
  const isValid = passwordHash.verify(password, user.passwordHash);

  if (!isValid) return false;

  const needsUpgrade = passwordHash.needsRehash(user.passwordHash, {
    iterations: 200000
  });

  if (needsUpgrade) {
    user.passwordHash = passwordHash.generate(password, {
      iterations: 200000
    });
    save(user);
  }

  return true;
}

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

Сравнение параметров хеша

Для корректной совместимости библиотека должна уметь сравнивать текущие параметры с эталонными:

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

Если параметры отличаются от рекомендованных, хеш помечается как устаревший.

Хранение параметров внутри хеша

Наиболее устойчивый подход — самодостаточный хеш, содержащий всю информацию о конфигурации:

argon2id$v=19$m=65536,t=3,p=1$salt$hash

Такой формат позволяет:

  • не хранить параметры отдельно
  • автоматически определять алгоритм
  • поддерживать разные поколения хешей

Переход между алгоритмами

Смена алгоритма (например, с PBKDF2 на Argon2) является наиболее критическим изменением. Прямая совместимость невозможна, поэтому используется комбинация стратегий:

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

В этот период система должна уметь распознавать оба формата.

Типичные ошибки при изменении параметров

Неправильная работа с параметрами приводит к ряду проблем:

  • потеря возможности проверить старые пароли
  • массовый выход пользователей из системы
  • снижение безопасности из-за частичной несовместимости
  • отсутствие явной версии хеша

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

Структурированное хранение хешей

Для обеспечения совместимости используется единый формат хранения:

{
  "hash": "pbkdf2:v2:iterations=200000:salt.hash",
  "createdAt": "2025-01-01",
  "algorithm": "pbkdf2",
  "version": 2
}

Такой подход позволяет:

  • отслеживать эволюцию параметров
  • выполнять миграции
  • проводить аудит безопасности

Детекция устаревших хешей

Система может автоматически определять необходимость обновления:

  • сравнение версии алгоритма
  • сравнение параметров с текущими стандартами
  • проверка минимальных требований безопасности

Если хотя бы один параметр не соответствует текущей политике, хеш считается устаревшим и подлежит обновлению при следующей успешной аутентификации.

Смешанные окружения и совместимость

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

  • единый реестр конфигураций хеширования
  • централизованная библиотека Password-hash
  • строгая политика версионирования

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