Cost factor (rounds): что это и как влияет на производительность

В bcrypt основным параметром, определяющим сложность хеширования, является cost factor (его также называют rounds или log rounds). Он задаёт количество вычислительных итераций, которые выполняет алгоритм при создании хеша пароля.

Математически bcrypt использует показатель степени двойки:

2^{cost factor}

Это означает, что фактическая вычислительная сложность растёт экспоненциально при увеличении значения cost factor.

Например:

  • cost = 10 → 2¹⁰ = 1024 итерации
  • cost = 12 → 2¹² = 4096 итераций
  • cost = 14 → 2¹⁴ = 16384 итерации

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


Роль cost factor в bcrypt.js

В библиотеке bcrypt.js cost factor передаётся как второй аргумент функции генерации соли:

import bcrypt from 'bcryptjs';

const saltRounds = 10;
const password = 'user_password';

const hash = bcrypt.hashSync(password, saltRounds);

Здесь saltRounds определяет, насколько «дорогой» будет операция хеширования.

Важно, что bcrypt.js сначала генерирует соль, в которой уже закодирован cost factor, а затем использует её для вычисления хеша:

const salt = bcrypt.genSaltSync(12);
const hash = bcrypt.hashSync(password, salt);

В этом случае cost factor фиксируется внутри соли и становится неотъемлемой частью результата.


Как cost factor влияет на производительность

bcrypt специально спроектирован так, чтобы быть медленным. Это не недостаток, а механизм защиты от атак перебора паролей.

При увеличении cost factor растёт:

  1. Время генерации хеша
  2. Время проверки пароля
  3. Нагрузка на CPU
  4. Энергопотребление при массовых проверках

Экспоненциальный рост времени

Если условно принять:

  • cost = 10 → 50 мс
  • cost = 11 → 100 мс
  • cost = 12 → 200 мс

То увеличение cost на единицу удваивает время вычисления.

Это поведение можно выразить через зависимость:

T(c) k ^{c}

где:

  • T(c) — время выполнения
  • c — cost factor
  • k — константа, зависящая от железа

Почему нельзя выбирать слишком маленький cost

Низкий cost factor делает систему уязвимой к brute-force атакам.

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

Типичные последствия:

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

Например, при cost = 8 или 9 современные GPU способны проверять огромные объёмы вариантов практически в реальном времени.


Почему нельзя выбирать слишком большой cost

Слишком высокий cost factor приводит к обратной проблеме — деградации производительности системы.

В серверной среде это проявляется как:

  • рост времени ответа при логине
  • блокировка event loop в Node.js при синхронных вызовах bcrypt.js
  • увеличение нагрузки на CPU при пиковых запросах
  • возможность DoS-эффекта через массовые запросы авторизации

Пример критичной ситуации:

bcrypt.hashSync(password, 14);

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


Практическое влияние на сервер Node.js

bcrypt.js имеет синхронные и асинхронные версии функций.

Синхронная версия:

bcrypt.hashSync(password, 12);

Асинхронная версия:

bcrypt.hash(password, 12, (err, hash) => {
  // результат
});

Разница критична:

  • sync блокирует event loop
  • async использует пул потоков libuv

При высоком cost factor синхронный вариант становится узким местом всей системы.


Подбор оптимального cost factor

Выбор значения зависит от баланса:

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

Обычно используют диапазон:

  • 10 — минимально допустимый для современных систем
  • 11–12 — стандартный баланс
  • 13+ — усиленная защита при высокой чувствительности данных

Каждое увеличение на 1 удваивает стоимость вычислений, поэтому рост происходит не линейно, а экспоненциально.


Почему bcrypt использует log2-модель

Использование log2 для cost factor не случайно. Это позволяет:

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

Внутри bcrypt соль кодируется так, что cost factor является частью строки:

$2a$12$...

Здесь 12 — это и есть log2 cost factor.


Влияние железа на реальное время вычислений

Хотя формула задаёт теоретическую сложность, фактическое время зависит от:

  • CPU (количество ядер и частота)
  • оптимизаций libbcrypt
  • реализации bcrypt.js (чистый JS медленнее native)
  • нагрузки системы
  • параллельных операций

На современных серверах разница может быть следующей:

  • cost 10 → десятки миллисекунд
  • cost 12 → сотни миллисекунд
  • cost 14 → заметные задержки в UX

Масштабирование нагрузки и cost factor

В системах с высокой нагрузкой cost factor становится ключевым параметром архитектуры.

Если количество пользователей растёт, то суммарная нагрузка на bcrypt растёт линейно, но стоимость каждой операции — экспоненциально.

Это приводит к необходимости:

  • снижать cost factor
  • выносить хеширование в отдельные сервисы
  • использовать rate limiting на авторизацию
  • кэшировать результаты проверки (осторожно, только в отдельных случаях)

Ошибки при работе с cost factor

Типичные проблемы:

  1. Слишком низкий cost в продакшене после тестирования
  2. Использование sync API в серверном коде
  3. Отсутствие стандарта между микросервисами
  4. Попытка динамически увеличивать cost без миграции хешей
  5. Игнорирование роста аппаратной мощности со временем

Эволюция cost factor во времени

Безопасный cost factor не является постоянной величиной. Он должен пересматриваться по мере роста вычислительной мощности оборудования.

То, что было безопасно 10 лет назад, сегодня может быть недостаточно медленным, чтобы защитить от атак перебора.

Поэтому системы часто хранят cost factor внутри хеша и позволяют использовать разные значения одновременно, постепенно обновляя старые хеши при логине пользователя.


Связь cost factor и времени жизни пароля

bcrypt позволяет естественным образом «старить» хеши:

  • старые записи используют меньший cost
  • новые создаются с более высоким
  • при успешной аутентификации хеш пересоздаётся

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


Итоговая роль cost factor в архитектуре безопасности

Cost factor является не просто числом, а ключевым параметром, определяющим:

  • устойчивость к перебору
  • нагрузку на сервер
  • задержку аутентификации
  • стоимость инфраструктуры

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