bcrypt.js реализует адаптивную криптографическую функцию хеширования, основанную на Blowfish. Ключевая характеристика алгоритма — параметр cost factor, чаще называемый rounds или saltRounds. Он определяет количество итераций экспоненциального расширения ключа.
Каждое увеличение rounds на единицу увеличивает вычислительную сложность примерно в 2 раза. Это означает нелинейное падение производительности при росте значения параметра.
Формально зависимость времени вычисления можно описать как:
T ^{r}
где r — значение cost factor (rounds), T — время хеширования.
Эта экспоненциальная зависимость является ключевым фактором при проектировании систем аутентификации под нагрузкой.
bcrypt.js является чистой JavaScript-реализацией алгоритма, без использования нативных C++ биндингов. Это приводит к следующим характеристикам:
bcrypt (native)В контексте нагрузочного тестирования это критично: CPU-bound операции становятся основным ограничением throughput.
Хеширование выполняется синхронно или асинхронно через event loop с использованием очереди задач, что влияет на latency под высокой нагрузкой.
Throughput системы аутентификации определяется количеством операций хеширования в единицу времени:
=
где:
При увеличении rounds время t растёт экспоненциально, что приводит к резкому снижению throughput.
Типичная зависимость:
| rounds | относительное время | относительный throughput |
|---|---|---|
| 8 | 1× | 100% |
| 10 | 4× | 25% |
| 12 | 16× | 6.25% |
| 14 | 64× | ~1.5% |
Эти значения демонстрируют не линейное, а степенное ухудшение производительности.
При увеличении параллельных запросов к bcrypt.hash в
Node.js наблюдаются следующие эффекты:
Каждая операция блокирует CPU на длительное время. Даже при использовании асинхронного API event loop получает значительную нагрузку.
Асинхронные вызовы bcrypt.js формируют очередь, в которой операции выполняются последовательно на уровне потоков libuv.
При высоких rounds (12–14) CPU быстро достигает 100% загрузки даже при умеренном количестве запросов.
Среднее время ответа системы увеличивается не линейно, а скачкообразно при достижении порога насыщения.
Нагрузочное тестирование должно учитывать следующие параметры:
Базовый подход — фиксирование throughput при разных значениях rounds.
Пример тестового сценария на Node.js:
import bcrypt fr om "bcryptjs";
async function runBenchmark(rounds, iterations) {
const password = "test_password_123";
const start = process.hrtime.bigint();
for (let i = 0; i < iterations; i++) {
await bcrypt.hash(password, rounds);
}
const end = process.hrtime.bigint();
const durationMs = Number(end - start) / 1e6;
return {
rounds,
iterations,
durationMs,
opsPerSecond: (iterations / durationMs) * 1000
};
}
(async () => {
console.log(await runBenchmark(8, 50));
console.log(await runBenchmark(10, 50));
console.log(await runBenchmark(12, 50));
})();
bcrypt.js предоставляет два основных режима:
При нагрузочном тестировании синхронный режим используется только для измерения worst-case latency.
С увеличением rounds масштабирование системы требует не только увеличения CPU, но и изменения архитектуры:
При этом даже горизонтальное масштабирование не устраняет экспоненциальный рост стоимости одной операции.
Один из ключевых методов стабилизации throughput — ограничение числа одновременных bcrypt операций:
import pLimit fr om "p-lim it";
import bcrypt fr om "bcryptjs";
const lim it = pLimit(5);
async function safeHash(password, rounds) {
return lim it(() => bcrypt.hash(password, rounds));
}
Это предотвращает полное насыщение CPU и снижает вероятность collapse event loop.
В реальных системах используются следующие метрики:
При увеличении rounds наблюдается характерный профиль:
Зависимость между безопасностью и производительностью является компромиссом:
Оценка времени вычисления может быть представлена как:
T_{new} = T_{base} ^{(r - r_{base})}
где увеличение r даже на 1 приводит к удвоению стоимости операции.
В микросервисной архитектуре bcrypt становится узким местом:
При высокой нагрузке часто применяется стратегия:
bcrypt.js проигрывает нативной реализации по throughput:
Это усиливает эффект деградации под нагрузочным тестированием.
При увеличении нагрузки фиксируются следующие закономерности:
Поведение системы приближается к модели очереди M/M/1 с ограниченной обслуживающей способностью, где service time определяется экспоненциальной функцией rounds.