Rainbow tables и атаки по словарю: почему bcrypt защищает от них

Атака по словарю основана на предположении, что пользователи выбирают предсказуемые пароли. Вместо перебора всех возможных комбинаций злоумышленник использует заранее подготовленный список распространённых паролей: «123456», «password», «qwerty», имена, даты рождения и т.д.

Алгоритм атаки прост:

  1. Берётся список вероятных паролей
  2. Каждый пароль хэшируется тем же алгоритмом, что используется в системе
  3. Полученный хэш сравнивается с хранимым в базе
  4. При совпадении пароль считается найденным

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


Радужные таблицы (Rainbow Tables)

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

Суть:

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

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

  • экономия времени за счёт увеличения объёма памяти
  • возможность мгновенного подбора пароля для популярных хэшей
  • особенно эффективны против быстрых хэш-функций (например, MD5, SHA-1)

Главная уязвимость таких таблиц — одинаковые входные данные дают одинаковый хэш. Это позволяет переиспользовать результаты.


Проблема быстрых хэш-функций

Классические криптографические функции (MD5, SHA-1, SHA-256):

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

Это делает их крайне уязвимыми:

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

Основные механизмы защиты в bcrypt

Библиотека bcrypt.js реализует алгоритм bcrypt, разработанный специально для хранения паролей. Его устойчивость к атакам обеспечивается тремя ключевыми механизмами:

1. Соль (Salt)

Соль — это случайная строка, добавляемая к паролю перед хэшированием.

Пример:

password + random_salt → hash

Свойства:

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

Почему это важно:

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

2. Медленный алгоритм

bcrypt специально сделан медленным и ресурсоёмким.

Ключевой параметр — cost factor (work factor):

2^cost операций хэширования

Пример:

const bcrypt = require('bcryptjs');

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

Где 10 означает:

  • 2¹⁰ = 1024 итерации
  • увеличение на 1 удваивает время вычисления

Эффект:

  • атаки по словарю замедляются экспоненциально
  • даже при использовании GPU скорость резко падает
  • стоимость атаки становится экономически невыгодной

3. Адаптивность

bcrypt позволяет увеличивать сложность со временем:

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

Это делает алгоритм «долгоживущим» и устойчивым к прогрессу железа.


Почему bcrypt защищает от атак по словарю

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

1. Отсутствие переиспользования результатов

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

2. Высокая стоимость каждой попытки

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

3. Ограниченная масштабируемость

  • bcrypt плохо распараллеливается
  • GPU не даёт такого прироста, как для SHA
  • атака требует больше ресурсов

Почему радужные таблицы не работают против bcrypt

Для эффективной работы радужных таблиц нужны:

  • детерминированность (одинаковый вход → одинаковый выход)
  • отсутствие случайности
  • быстрые вычисления

bcrypt нарушает все три условия:

Свойство bcrypt
Детерминированность Нет (из-за соли)
Повторяемость Нет
Скорость Низкая

Следствие:

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

Практический пример атаки

Без bcrypt (например, SHA-256):

  • 1 миллиард хэшей/сек
  • словарь: 10 млн паролей
  • время: доли секунды

С bcrypt (cost = 10):

  • ~100 хэшей/сек
  • словарь: 10 млн
  • время: более суток

С bcrypt (cost = 12):

  • ~25 хэшей/сек
  • время: недели

Рост сложности делает атаку практически бессмысленной.


Структура bcrypt-хэша

bcrypt хранит всю необходимую информацию в строке:

$2a$10$E9N6n5...rest_of_hash

Расшифровка:

  • $2a$ — версия алгоритма
  • 10 — cost factor
  • далее — соль + хэш

Это позволяет:

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

Проверка пароля

bcrypt не сравнивает строки напрямую. Он повторно хэширует ввод пользователя с теми же параметрами:

bcrypt.compareSync('input_password', stored_hash);

Внутри:

  1. извлекается соль и cost
  2. выполняется хэширование
  3. сравниваются результаты

Ограничения и важные нюансы

Несмотря на устойчивость, bcrypt не является абсолютной защитой:

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

Рекомендуемые практики:

  • использовать cost не ниже 10–12
  • применять ограничения на попытки входа
  • внедрять двухфакторную аутентификацию
  • проверять сложность паролей

Сравнение с другими подходами

Метод Устойчивость к словарю Устойчивость к rainbow tables
MD5 Низкая Нет
SHA-256 Низкая Нет
bcrypt Высокая Полная
scrypt Очень высокая Полная
Argon2 Очень высокая Полная

bcrypt остаётся популярным благодаря:

  • простоте
  • проверенной надёжности
  • широкой поддержке

Ключевая идея защиты

bcrypt не делает подбор невозможным — он делает его невыгодным:

  • по времени
  • по ресурсам
  • по стоимости

Именно это превращает атаки по словарю и радужные таблицы из практического инструмента в теоретическую возможность.