Ограничение длины входного пароля: атака на bcrypt и аналогии

Одним из ключевых нюансов при работе с алгоритмами хеширования паролей является ограничение длины входных данных, которое напрямую влияет на безопасность системы. В контексте bcrypt и его реализаций в JavaScript через различные библиотеки (включая обобщённые password-hash решения), это ограничение становится источником потенциальных атак, связанных с предсказуемостью и усечением паролей.


Поведение bcrypt при обработке длинных паролей

Алгоритм bcrypt исторически имеет фиксированное ограничение на длину входной строки — 72 байта. Это означает, что:

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

Ключевой эффект:

password123 + (любые дополнительные символы после 72 байт) → один и тот же хеш

Это свойство не является багом реализации конкретной библиотеки JavaScript, а обусловлено самим дизайном bcrypt.


Практическое проявление проблемы в JavaScript-библиотеках

При использовании библиотек уровня password-hash, bcrypt или обёрток над ними в Node.js возникает скрытая опасность:

const bcrypt = require('bcrypt');

const password1 = 'A'.repeat(72) + 'X';
const password2 = 'A'.repeat(72) + 'Y';

const hash1 = bcrypt.hashSync(password1, 10);
const hash2 = bcrypt.hashSync(password2, 10);

console.log(hash1 === hash2); // true

Несмотря на различие входных паролей, результат совпадает.


Уязвимость как следствие усечения

Ограничение длины создаёт классическую проблему:

1. Потеря энтропии

После 72 байт дополнительная информация не учитывается. Это означает, что:

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

2. Коллизии по усечённому пространству

Разные пароли становятся эквивалентными:

  • password + AAAAA...
  • password + BBBBB...

Если первые 72 байта совпадают, хеш совпадает.

3. Атаки перебора с нормализацией

Атакующий может оптимизировать перебор, сокращая пространство поиска до первых 72 байт.


Аналогии с другими криптографическими системами

Аналогия с обрезанием ключа

Это можно сравнить с ситуацией, когда замок принимает только первые 10 символов ключа:

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

Аналогия с контрольной суммой фиксированной длины

Представим систему, где:

  • берётся только часть входного сообщения,
  • остальное отбрасывается,
  • контрольная сумма вычисляется только по усечённой части.

Это приводит к тому, что:

разные сообщения → одинаковая контрольная сумма


Аналогия с укороченной подписью документа

Если юридическая подпись учитывает только первые символы имени:

  • «Александр Иванович Петров»
  • «Александр Иванович Сидоров»

при одинаковых первых частях могут считаться идентичными в системе проверки.


Почему ограничение в bcrypt существует

Историческая причина связана не с безопасностью как таковой, а с:

  • ограничениями оригинального алгоритма Blowfish;
  • фиксированным размером блока обработки;
  • стремлением сохранить совместимость и предсказуемость реализации.

bcrypt использует ключевое расширение на основе Blowfish, где:

входной пароль преобразуется в ключ фиксированной длины


Поведение библиотек password-hash в JavaScript

Многие библиотеки, предоставляющие высокий уровень абстракции (вроде password-hash wrappers), часто:

  • не документируют явно лимит 72 байта;
  • полагаются на underlying bcrypt;
  • создают ложное ощущение отсутствия ограничений.

Типичный интерфейс:

const passwordHash = require('password-hash');

const hash = passwordHash.generate('superSecretPassword');
const result = passwordHash.verify('superSecretPassword', hash);

Проблема возникает, когда разработчик предполагает:

  • отсутствие ограничений длины;
  • линейную зависимость результата от всего пароля.

Влияние кодировки UTF-8

Важно учитывать, что лимит bcrypt измеряется в байтах, а не символах:

  • ASCII символ = 1 байт;
  • кириллица = обычно 2 байта;
  • эмодзи = 4 байта.

Следствие:

пароль из 72 символов может превышать лимит в 72 байта

Это создаёт скрытое несоответствие между ожиданием и фактической обработкой.


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

1. Предсказуемость хеша

Если атакующий знает, что используется bcrypt:

  • он может игнорировать часть пароля;
  • сокращать пространство перебора.

2. Уязвимость при длинных паролях

Иронично, но длинные пароли могут:

  • не увеличивать безопасность;
  • создавать ложное ощущение защищённости.

3. Несовместимость между системами

Разные библиотеки могут:

  • обрезать строку по-разному;
  • учитывать разные кодировки;
  • приводить к расхождениям при миграции данных.

Сравнение с альтернативными алгоритмами

bcrypt

  • фиксированный лимит 72 байта;
  • усечение входа;
  • высокая распространённость.

scrypt / argon2

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

Модель атаки через усечение

Рассмотрим упрощённую модель:

  1. Пользователь задаёт пароль длиной 100 байт.
  2. Система использует bcrypt.
  3. Хешируется только первые 72 байта.
  4. Атакующий подбирает только эту часть.

Результат:

эффективная стойкость пароля ниже заявленной длины


Инженерный вывод из свойства усечения

Ограничение длины входа в bcrypt приводит к системному эффекту:

  • безопасность определяется не всей строкой,
  • а только её начальной частью,
  • что нарушает интуитивную модель «длиннее = безопаснее».

Это особенно важно при проектировании систем авторизации в JavaScript, где обёртки над bcrypt скрывают внутренние ограничения и создают иллюзию линейной криптографической стойкости.