Поведение при передаче неверных типов и null

Password-hash в прикладной разработке часто рассматривается как слой, принимающий строго определённый тип входных данных: строку пароля. Любое отклонение от ожидаемого типа — null, undefined, числа, объекты, массивы или «пустые» значения — приводит к поведению, которое критично понимать при построении безопасных систем аутентификации.

Большинство реализаций password-hash в JavaScript строятся вокруг предположения:

  • пароль представлен непустой строкой
  • кодировка строки корректна (обычно UTF-8)
  • входное значение не требует дополнительных преобразований типов

При нарушении этих условий библиотека не может гарантировать корректность хеширования, так как алгоритмы (bcrypt, scrypt, argon2 или их обёртки) оперируют байтовыми представлениями строк.

Передача null и undefined

Поведение на уровне вызова функции

При передаче null или undefined в функции хеширования возможны три типичных сценария:

1. Явная ошибка типа Многие реализации выполняют строгую проверку:

  • if (password == null) → выброс исключения
  • типовая ошибка: TypeError: password must be a string

Такой подход считается наиболее безопасным, так как предотвращает случайное хеширование пустых значений.

2. Неявное преобразование в строку Некоторые реализации приводят значение к строке:

  • String(null)"null"
  • String(undefined)"undefined"

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

3. Возврат ошибки через промис В асинхронных API часто используется модель:

  • Promise.reject(new Error(...))

Это характерно для реализаций, ориентированных на bcrypt/argon2, где вычисления вынесены в поток или нативный биндинг.

Неверные типы данных

Числа

Передача чисел (12345) обычно приводит к одному из вариантов:

  • преобразование в строку "12345"
  • ошибка валидации типа
  • непредсказуемое поведение при внутренней сериализации

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

Объекты

При передаче объектов ({ password: "123" }) поведение зависит от реализации:

  • "[object Object]" после приведения к строке
  • выброс ошибки сериализации
  • игнорирование входа и возврат исключения

Особенно опасны случаи, когда объект неявно приводится к строке, создавая одинаковый хеш для всех объектов.

Массивы

Массивы (["a", "b"]) обычно преобразуются через toString():

  • ["a","b"] → "a,b"

Это может привести к неожиданным коллизиям, если разработчик не контролирует типизацию на уровне API.

Пустые строки и whitespace

Отдельный класс проблем возникает при передаче:

  • "" (пустая строка)
  • " " (пробел)
  • "\n\t"

Поведение библиотеки обычно одно из следующих:

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

В контексте безопасности чаще применяется запрет на пустые значения, так как они увеличивают риск слабых учётных записей.

Неявные преобразования и JavaScript coercion

JavaScript допускает неявные преобразования типов, что особенно критично для password-hash логики:

  • null + "" → "null"
  • [] + "" → ""
  • {} + “” → “[object Object]”

Если библиотека не блокирует coercion, возможны:

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

Защитные механизмы библиотек

Качественные реализации password-hash обычно включают:

Явную валидацию типа

  • typeof password !== "string"
  • проверка password.length > 0

Нормализацию входа

  • приведение к Unicode NFC/NFKC
  • удаление управляющих символов (в некоторых конфигурациях)

Жёсткое отклонение некорректных значений

  • fail-fast стратегия
  • немедленный throw без попытки обработки

Асинхронные сценарии и побочные эффекты

В асинхронных реализациях (особенно с argon2/bcrypt bindings) некорректные типы могут проявляться иначе:

  • ошибка возникает в worker thread
  • исключение возвращается через rejected promise
  • возможны «тихие» падения при неправильной обработке .catch

Неправильная обработка таких случаев приводит к тому, что пользователь получает неконсистентное состояние: пароль не установлен, но операция считается успешной на уровне бизнес-логики.

Потенциальные уязвимости при отсутствии валидации

Игнорирование проверки типов создаёт несколько классов проблем:

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

Особенно опасен сценарий, когда null и пустая строка обрабатываются одинаково, что позволяет создавать идентичные хеши для разных состояний данных.

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

На практике большинство библиотек следуют одному из двух подходов:

Строгий режим:

  • любые не-строки → ошибка
  • null/undefined → throw
  • пустая строка → reject

Гибкий режим:

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

Первый вариант используется в security-critical библиотеках, второй — в утилитарных обёртках.

Итоговые поведенческие модели

Поведение при неверных типах и null можно свести к трём моделям:

  • Fail-fast модель — полное запрещение некорректных входов
  • Coercion-модель — приведение всех значений к строке
  • Hybrid-модель — частичная проверка с мягкими преобразованиями

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