Синхронное хеширование: bcrypt.hashSync

Общие принципы работы bcrypt

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

Ключевая особенность bcrypt — адаптивная сложность. Это означает, что со временем можно увеличивать стоимость вычислений (work factor), делая подбор паролей всё более дорогим по ресурсам.

В библиотеке bcrypt.js существуют два основных подхода:

  • синхронный (blocking)
  • асинхронный (non-blocking)

Синхронный вариант реализуется через метод bcrypt.hashSync, который выполняет хеширование немедленно, блокируя поток выполнения JavaScript до завершения операции.


Сигнатура метода bcrypt.hashSync

Базовая форма вызова:

bcrypt.hashSync(data, saltOrRounds)

Параметры:

data

  • Тип: string
  • Содержимое: исходный пароль, который необходимо захешировать

saltOrRounds

  • Тип: string | number

  • Возможные значения:

    • строка соли (salt), сгенерированная заранее
    • число раундов (cost factor), по которому будет автоматически создан salt

Принцип работы синхронного хеширования

При вызове bcrypt.hashSync происходит несколько этапов:

  1. Проверка входных данных
  2. Генерация соли (если передано число)
  3. Применение итеративной функции EksBlowfish
  4. Формирование итогового хеша
  5. Возврат результата в виде строки

Вся операция выполняется в одном потоке, без передачи управления event loop до завершения вычислений.


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

Базовое хеширование пароля

const bcrypt = require('bcryptjs');

const password = 'mySuperPassword123';
const hash = bcrypt.hashSync(password, 10);

console.log(hash);

В этом примере:

  • пароль преобразуется в хеш
  • число 10 означает количество раундов (cost factor)

Параметр rounds (cost factor)

Число раундов определяет сложность вычислений.

bcrypt.hashSync(password, 12);

Влияние значения:

  • 8 — быстро, низкая безопасность
  • 10 — стандартный баланс
  • 12+ — высокая безопасность, но значительная нагрузка
  • 14+ — используется редко, только при высоких требованиях

Каждое увеличение на 1 удваивает вычислительную сложность.


Использование заранее сгенерированной соли

bcrypt позволяет передать готовую соль:

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

Особенности:

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

Структура результата хеширования

Результат bcrypt.hashSync выглядит как строка:

$2a$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36b8Zc1v2h4gkq8Z8n4l2mW

Разбор структуры:

  • $2a$ — версия алгоритма
  • 10 — cost factor
  • далее — соль и зашифрованный результат

Почему используется синхронный метод

Синхронное хеширование применяется в случаях, когда:

  • требуется простая логика без callback или Promise
  • операция выполняется однократно (например, скрипты миграции)
  • отсутствует высокая нагрузка на сервер

Ограничения синхронного хеширования

Блокировка event loop

Node.js выполняет JavaScript в одном потоке. При вызове hashSync:

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

Пример негативного эффекта

app.post('/register', (req, res) => {
  const hash = bcrypt.hashSync(req.body.password, 12);
  // сервер "заморожен" во время вычисления
  res.send(hash);
});

При множественных запросах это приводит к деградации производительности.


Сравнение с асинхронной версией

Синхронный метод:

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

Асинхронный метод:

bcrypt.hash(password, 10, (err, hash) => {
  console.log(hash);
});

Разница:

Параметр hashSync hash (async)
Блокировка потока да нет
Производительность ниже выше
Удобство выше ниже

Внутренние особенности реализации

bcrypt.js использует реализацию алгоритма EksBlowfish, который включает:

  • расширение ключа
  • множество итераций шифрования
  • зависимость от salt и cost factor

Синхронная версия оборачивает этот процесс в блокирующий вызов.


Практические сценарии использования

Скрипты генерации паролей

const users = ['admin', 'test', 'demo'];

const result = users.map(u => ({
  username: u,
  passwordHash: bcrypt.hashSync('defaultPass123', 10)
}));

Миграция базы данных

for (let user of users) {
  user.password = bcrypt.hashSync(user.password, 12);
}

Частые ошибки при использовании

Использование слишком высокого cost factor

bcrypt.hashSync(password, 20);

Это может привести к:

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

Хеширование в горячих маршрутах API

app.post('/login', (req, res) => {
  const hash = bcrypt.hashSync(req.body.password, 12);
});

Ошибка заключается в блокировке каждого запроса.


Повторное хеширование уже захешированного значения

bcrypt не предназначен для повторного хеширования своей же строки.


Оптимальные практики

  • использовать cost factor 10–12 для большинства систем
  • избегать hashSync в API-серверах
  • применять синхронный метод в административных и офлайн-скриптах
  • учитывать нагрузку CPU при массовых операциях

Влияние синхронного хеширования на производительность Node.js

Каждый вызов:

  • блокирует event loop
  • останавливает обработку очереди событий
  • увеличивает latency всех запросов

При масштабировании системы это становится критическим ограничением.


Поведение при многопоточности

Node.js не использует многопоточность для JavaScript-кода, поэтому:

  • 1 вызов hashSync = блокировка всего процесса
  • Worker Threads могут частично решить проблему, но не используются автоматически

Взаимодействие с другими частями системы

При использовании bcrypt.hashSync важно учитывать:

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

Сравнение производительности при разных раундах

rounds приблизительное время
8 быстро
10 умеренно
12 заметная задержка
14 высокая нагрузка

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


Особенности генерации соли внутри hashSync

Если передано число:

bcrypt.hashSync(password, 10);

выполняется:

  • генерация случайной соли
  • кодирование параметров
  • объединение с паролем

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

Даже при одинаковом пароле:

bcrypt.hashSync('123456', 10);
bcrypt.hashSync('123456', 10);

результаты будут различаться из-за случайной соли.


Использование в тестировании

Синхронный метод часто применяется в:

  • unit-тестах
  • генерации фикстур
  • подготовке тестовых пользователей

Причина — простота и отсутствие асинхронной логики.


Влияние на безопасность

bcrypt.hashSync обеспечивает:

  • защиту от rainbow table атак
  • устойчивость к brute force
  • адаптивную сложность

Безопасность определяется не методом (sync/async), а параметрами salt и rounds.