Библиотека bcrypt.js работает исключительно со строковыми данными, однако важно понимать, что на уровне алгоритма хеширования происходит преобразование входной строки в байтовую последовательность. Это означает, что итоговый результат зависит не только от логического содержимого пароля, но и от его внутреннего представления в памяти.
В JavaScript строки представлены в UTF-16, тогда как bcrypt изначально проектировался с расчётом на байтовые последовательности, чаще всего интерпретируемые через UTF-8. bcrypt.js выполняет внутреннее преобразование, но это не исключает ряда тонких проблем, связанных с кодировками и нормализацией строк.
Одна из ключевых проблем заключается в том, что визуально идентичные строки могут иметь разное байтовое представление.
Например:
Визуально строки одинаковы, но их бинарное представление различается. bcrypt.js будет хешировать именно байтовую форму строки, а не её визуальное отображение.
Это приводит к тому, что:
Для устранения неоднозначностей используется Unicode Normalization Form C (NFC) или Form D (NFD). В большинстве случаев рекомендуется NFC как более предсказуемая форма.
Типичная проблема возникает в сценариях:
bcrypt.js не выполняет нормализацию автоматически, поэтому ответственность полностью ложится на уровень приложения.
Рекомендуемый подход:
JavaScript-строки используют UTF-16, но большинство серверных систем и криптографических алгоритмов ожидают UTF-8.
bcrypt.js внутри выполняет преобразование строки в байтовый массив, однако это преобразование не всегда интуитивно прозрачно.
Проблемные сценарии:
Такие символы могут:
Одна из наиболее опасных категорий проблем — скрытые символы:
bcrypt.js не выполняет очистку входных данных, поэтому каждая такая деталь становится частью хеша.
Это приводит к ситуациям, когда:
В реальных приложениях пароль может проходить через несколько слоёв:
На каждом этапе возможны изменения строки:
bcrypt.js получает уже «готовую» строку, но не контролирует её происхождение.
Критическая ошибка архитектуры — разное преобразование пароля на этапе регистрации и на этапе проверки.
bcrypt.js используется как в браузере, так и в Node.js, и это создаёт дополнительные расхождения:
Особенно заметны проблемы при:
Распространённая ошибка — повторное преобразование строки перед передачей в bcrypt:
Такие операции могут:
bcrypt.js не способен отличить исходный пароль от уже модифицированного представления, он работает строго с тем, что ему передано.
bcrypt.compare выполняет сравнение хеша с входной строкой, но если строка уже отличается на уровне байтов, результат всегда будет отрицательным.
Типичные причины ошибок:
Для минимизации проблем на уровне кодировок используется строгий пайплайн обработки:
Особое внимание требуется уделять:
Некорректная обработка кодировок может привести не только к ошибкам, но и к уязвимостям:
В редких случаях это может приводить к логическим ошибкам аутентификации, особенно в системах с несколькими источниками регистрации пользователей.
bcrypt.js гарантирует одинаковый результат только при идентичной входной строке на уровне байтов.
Любое отклонение в:
приводит к совершенно другому хешу, даже если пользователь воспринимает ввод как одинаковый.
Это фундаментальное свойство, которое делает bcrypt устойчивым к атаке перебора, но одновременно повышает требования к корректной обработке входных данных на уровне приложения.