Одним из ключевых нюансов при работе с алгоритмами хеширования паролей является ограничение длины входных данных, которое напрямую влияет на безопасность системы. В контексте bcrypt и его реализаций в JavaScript через различные библиотеки (включая обобщённые password-hash решения), это ограничение становится источником потенциальных атак, связанных с предсказуемостью и усечением паролей.
Алгоритм bcrypt исторически имеет фиксированное ограничение на длину входной строки — 72 байта. Это означает, что:
Ключевой эффект:
password123+ (любые дополнительные символы после 72 байт) → один и тот же хеш
Это свойство не является багом реализации конкретной библиотеки JavaScript, а обусловлено самим дизайном bcrypt.
При использовании библиотек уровня 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
Несмотря на различие входных паролей, результат совпадает.
Ограничение длины создаёт классическую проблему:
После 72 байт дополнительная информация не учитывается. Это означает, что:
Разные пароли становятся эквивалентными:
password + AAAAA...password + BBBBB...Если первые 72 байта совпадают, хеш совпадает.
Атакующий может оптимизировать перебор, сокращая пространство поиска до первых 72 байт.
Это можно сравнить с ситуацией, когда замок принимает только первые 10 символов ключа:
Представим систему, где:
Это приводит к тому, что:
разные сообщения → одинаковая контрольная сумма
Если юридическая подпись учитывает только первые символы имени:
при одинаковых первых частях могут считаться идентичными в системе проверки.
Историческая причина связана не с безопасностью как таковой, а с:
bcrypt использует ключевое расширение на основе Blowfish, где:
входной пароль преобразуется в ключ фиксированной длины
Многие библиотеки, предоставляющие высокий уровень абстракции (вроде password-hash wrappers), часто:
Типичный интерфейс:
const passwordHash = require('password-hash');
const hash = passwordHash.generate('superSecretPassword');
const result = passwordHash.verify('superSecretPassword', hash);
Проблема возникает, когда разработчик предполагает:
Важно учитывать, что лимит bcrypt измеряется в байтах, а не символах:
Следствие:
пароль из 72 символов может превышать лимит в 72 байта
Это создаёт скрытое несоответствие между ожиданием и фактической обработкой.
Если атакующий знает, что используется bcrypt:
Иронично, но длинные пароли могут:
Разные библиотеки могут:
Рассмотрим упрощённую модель:
Результат:
эффективная стойкость пароля ниже заявленной длины
Ограничение длины входа в bcrypt приводит к системному эффекту:
Это особенно важно при проектировании систем авторизации в JavaScript, где обёртки над bcrypt скрывают внутренние ограничения и создают иллюзию линейной криптографической стойкости.