Версионирование алгоритма в схеме хранения

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

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

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

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

Структура хранения хеша с учётом версии

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

Типичная структура включает:

  • идентификатор алгоритма;
  • параметры вычисления (cost factor, memory, iterations);
  • соль;
  • итоговый хеш.

Пример строкового представления:

$argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>

или

$2b$12$<salt><hash>

Такая форма позволяет системе определить, каким способом проверять пароль, без обращения к внешним таблицам версий.


Подходы к версионированию

1. Встроенная версия в строке хеша

Наиболее распространённый подход — включение версии алгоритма непосредственно в строку хеша.

Принцип работы:

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

Преимущества:

  • автономность хеша;
  • отсутствие зависимости от внешней схемы БД;
  • простота миграции между алгоритмами.

Недостатки:

  • увеличение длины строки;
  • необходимость строгого формата парсинга.

2. Версия алгоритма в базе данных

Альтернативный подход — хранение версии алгоритма в отдельном поле:

{
  "password_hash": "<hash>",
  "hash_version": 2
}

Логика проверки:

  1. извлекается версия;
  2. выбирается соответствующий алгоритм;
  3. выполняется проверка.

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

Преимущества:

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

Недостатки:

  • зависимость от схемы БД;
  • риск рассинхронизации логики и данных.

3. Гибридная модель

Комбинированный подход включает:

  • хранение версии в строке хеша;
  • дублирование метаданных в базе данных.

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


Роль библиотеки Password-hash в JavaScript

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

Типичная логика:

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

Пример использования:

import { hash, verify } from "password-hash";

const hashed = hash("user_password");
// hashed содержит алгоритм, соль и параметры

const isValid = verify("user_password", hashed);

Версионирование в данном контексте реализуется через структуру самого хеша, а не через внешние механизмы.


Миграция между версиями алгоритмов

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

Ленивое обновление (lazy rehash)

Принцип:

  • пользователь входит в систему;
  • пароль проверяется старым алгоритмом;
  • при успешной проверке хеш пересоздаётся новым алгоритмом.

Псевдологика:

if (verify(password, storedHash)) {
  if (needsRehash(storedHash)) {
    const newHash = hash(password);
    updateUserPassword(newHash);
  }
}

Ключевым элементом является функция needsRehash, определяющая устаревание версии.


Определение устаревших версий

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

Критерии:

  • изменение алгоритма (bcrypt → argon2);
  • снижение параметров сложности (cost factor);
  • устаревание криптографического стандарта;
  • изменение политики безопасности.

Пример проверки:

function needsRehash(hashString) {
  const meta = parseHash(hashString);

  return (
    meta.algorithm !== "argon2id" ||
    meta.memory < 65536 ||
    meta.iterations < 3
  );
}

Совместимость версий алгоритмов

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

  • анализ префикса хеша;
  • выбор соответствующего адаптера алгоритма;
  • единый интерфейс проверки.

Архитектурно это реализуется через стратегию (Strategy Pattern), где каждый алгоритм представляет отдельную реализацию.


Ошибки при проектировании версионирования

Распространённые проблемы:

  • отсутствие версии в хеше, приводящее к невозможности миграции;
  • жесткая привязка к одному алгоритму;
  • хранение параметров вне хеша без резервирования структуры;
  • отсутствие механизма rehash при логине;
  • несовместимость форматов между средами (Node.js версии, различия библиотек).

Роль параметров сложности в версии алгоритма

Версионирование включает не только сам алгоритм, но и его конфигурацию. Для современных KDF (Key Derivation Function) критичны параметры:

  • memory cost;
  • time cost;
  • parallelism;
  • salt length.

Изменение любого параметра фактически формирует новую версию схемы хеширования.


Интерпретация версии в строковом формате

Многие алгоритмы используют структурированный формат строки:

  • $argon2id$ — идентификатор алгоритма;
  • v=19 — версия спецификации;
  • параметры m, t, p — конфигурация;
  • соль и хеш в base64.

Такая структура позволяет системе не хранить отдельное поле версии, сохраняя всё в одном поле базы данных.


Расширяемость схемы хранения

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

  • строгий формат сериализации;
  • независимость парсера от конкретного алгоритма;
  • регистрацию алгоритмов в реестре поддерживаемых стратегий.

Пример реестра:

const algorithms = {
  bcrypt: bcryptAdapter,
  argon2id: argon2Adapter,
  pbkdf2: pbkdf2Adapter
};

Контроль жизненного цикла алгоритмов

Каждый алгоритм проходит стадии:

  • внедрение;
  • активное использование;
  • режим совместимости;
  • депрецированное состояние;
  • удаление поддержки.

Версионирование позволяет управлять этими стадиями без нарушения работы системы аутентификации.