Политики паролей в связке с валидацией до хеширования

Политика паролей должна определяться до момента хеширования, а не после него. Любая попытка «сначала захешировать, потом проверить» приводит к фиксации слабых, предсказуемых или даже намеренно вредных значений в базе, после чего исправить ситуацию становится значительно сложнее. В связке с bcrypt.js это особенно важно, потому что алгоритм рассчитан на дорогую вычислительную операцию, и повторное изменение стратегии защиты уже существующих паролей требует миграции данных или принудительной смены паролей.

bcrypt.js применяется как реализация адаптивного хеширования паролей, где ключевым параметром является cost factor (число раундов соль+хеширования). Но сам по себе bcrypt не решает задачу качества пароля. Он лишь гарантирует, что даже хороший или плохой пароль будет храниться в виде устойчивого к перебору хеша. Поэтому вся логика «допуска или запрета» должна выполняться до вызова hash.


Перед тем как пароль попадёт в bcrypt, он должен пройти несколько уровней проверки:

  • синтаксическая валидация (тип, длина, допустимые символы)
  • проверка политики безопасности (сложность, запрещённые шаблоны)
  • нормализация строки (Unicode, пробелы, визуально одинаковые символы)
  • проверка на очевидную компрометацию (часто используемые пароли)

bcrypt.js не участвует в этих этапах. Его задача начинается строго после того, как входное значение признано допустимым.


Базовая политика паролей

Минимальный уровень политики обычно включает:

  • длина не менее 8–12 символов
  • отсутствие полностью числовых или полностью буквенных последовательностей
  • запрет на очевидные комбинации (12345678, password, qwerty и т.п.)
  • обязательное разнообразие символов (но без избыточного усложнения требований)

Чрезмерно строгие правила (например, обязательные символы всех категорий) часто приводят к обратному эффекту — пользователи начинают генерировать предсказуемые шаблоны вроде Aa1!Aa1!.


Валидация до bcrypt.hash

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

  1. Получение пароля из запроса
  2. Проверка политики безопасности
  3. Нормализация строки
  4. Только после этого — хеширование через bcrypt.js

Пример проверки перед хешированием:

function validatePassword(password) {
  if (typeof password !== 'string') {
    throw new Error('Пароль должен быть строкой');
  }

  if (password.length < 10) {
    throw new Error('Пароль слишком короткий');
  }

  if (/^\d+$/.test(password)) {
    throw new Error('Пароль не должен состоять только из цифр');
  }

  if (/^(password|123456|qwerty)$/i.test(password)) {
    throw new Error('Слишком распространённый пароль');
  }

  return true;
}

Только после успешного прохождения проверки выполняется хеширование:

import bcrypt from 'bcryptjs';

async function createPasswordHash(password) {
  validatePassword(password);

  const saltRounds = 12;
  const hash = await bcrypt.hash(password, saltRounds);

  return hash;
}

Почему нельзя валидировать после хеширования

Хеш bcrypt необратим. После выполнения операции:

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

Если политика меняется, единственный путь — заставить пользователя сменить пароль. Поэтому логика контроля должна находиться строго до хеширования.


Нормализация паролей

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

Проблемы, которые решает нормализация:

  • визуально одинаковые символы в разных Unicode-формах
  • невидимые пробелы
  • комбинированные символы (например, акценты)

Пример:

function normalizePassword(password) {
  return password
    .normalize('NFKC')
    .trim();
}

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


Проверка утечек и слабых паролей

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

Распространённые подходы:

  • локальные списки запрещённых паролей
  • проверка через внешние API утечек
  • словари популярных комбинаций

Логика остаётся той же: проверка до хеширования.


Настройка bcrypt.js в контексте политики

bcrypt.js предоставляет параметр cost factor:

const saltRounds = 10;

Повышение значения:

  • увеличивает вычислительную стоимость
  • замедляет brute-force атаки
  • увеличивает нагрузку на сервер

Но важно понимать: увеличение saltRounds не компенсирует слабый пароль. Это лишь замедляет взлом, но не устраняет саму уязвимость.


Связка политики и адаптивной сложности

Современные системы часто комбинируют:

  • фиксированную минимальную политику
  • динамическую проверку по контексту (например, корпоративные требования)
  • адаптивный bcrypt cost factor

Пример подхода:

  • обычные пользователи: 10–12 rounds
  • повышенная безопасность: 12–14 rounds
  • критические системы: выше, но с учётом нагрузки

При этом политика паролей остаётся неизменной для всех уровней — меняется только стоимость хеширования.


Ошибки реализации

Распространённые ошибки при работе с bcrypt.js:

1. Хеширование до валидации Пароль сразу передаётся в bcrypt без проверки качества.

2. Проверка хеша вместо пароля Попытка анализировать результат bcrypt.hash для оценки сложности.

3. Отсутствие нормализации Одинаковые по смыслу пароли дают разные хеши из-за Unicode-различий.

4. Слишком слабая политика Ограничение только длины без проверки предсказуемости.

5. Жёсткая политика без анализа UX Пользователи обходят правила предсказуемыми шаблонами.


Обновление политики и миграция

При изменении требований возникает проблема уже сохранённых паролей. bcrypt.js решает это частично через повторное хеширование при входе:

if (bcrypt.getRounds(hash) < 12) {
  const newHash = await bcrypt.hash(password, 12);
}

Таким образом, политика постепенно обновляется без массового сброса паролей.


Структура безопасного потока обработки

Корректная последовательность:

  1. Получение пароля
  2. Нормализация строки
  3. Проверка политики безопасности
  4. Проверка на утечки
  5. Определение cost factor
  6. bcrypt.hash
  7. Сохранение результата

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


Ключевой принцип

bcrypt.js отвечает за стойкость хранения, но не за качество входных данных. Политика паролей — это слой до криптографии, а не внутри неё. Чем раньше происходит отсеивание слабых значений, тем меньше вычислительных и организационных затрат возникает в дальнейшем.