Laravel: Hash::check() возвращает `true` для двух разных сообщений

#laravel #hash #bcrypt

#laravel #хэш #bcrypt

Вопрос:

Я наблюдал очень странное поведение с использованием Hash фасада Laravel Hash::make() для создания дайджеста (с bcrypt ) и сохранения его в базе данных. Например, обычный текст

AAMkAGEzN2EyZTg4LWRiNTUtNGIwYS04ZTA1LWE2Y2U5OTRjYjQ0ZgBGAAAAAACxCzc14g3eSoadAxaGpB3ABwCr5qkyxHH4QY9vHKr6u5IrAAAAAAENAACr5qkyxHH4QY9vHKr6u5IrAARi2BmGAAA=

выдает $2y$10$fq6jvoNL/RShVKfNDy64EOGW0gLzd0GvfS.di16Z9LcCK7DpIHONK результат .

Теперь, при использовании Hash::check() с обычным текстом и дайджестом, упомянутым выше, возвращает true , конечно. Однако изменение одного символа в обычном тексте (например, замена последнего A на a B ) и проверка его на соответствие тому же дайджесту true также возвращает:

 >>> Hash::check('AAMkAGEzN2EyZTg4LWRiNTUtNGIwYS04ZTA1LWE2Y2U5OTRjYjQ0ZgBGAAAAAACxCzc14g3eSoadAxaGpB3ABwCr5qkyxHH4QY9vHKr6u5IrAAAAAAENAACr5qkyxHH4QY9vHKr6u5IrAARi2BmGAAA=', '$2y$10$fq6jvoNL/RShVKfNDy64EOGW0gLzd0GvfS.di16Z9LcCK7DpIHONK')
=> true
>>> Hash::check('AAMkAGEzN2EyZTg4LWRiNTUtNGIwYS04ZTA1LWE2Y2U5OTRjYjQ0ZgBGAAAAAACxCzc14g3eSoadAxaGpB3ABwCr5qkyxHH4QY9vHKr6u5IrAAAAAAENAACr5qkyxHH4QY9vHKr6u5IrAARi2BmGAAB=', '$2y$10$fq6jvoNL/RShVKfNDy64EOGW0gLzd0GvfS.di16Z9LcCK7DpIHONK')
=> true
>>> Hash::check('AAMkAGEzN2EyZTg4LWRiNTUtNGIwYS04ZTA1LWE2Y2U5OTRjYjQ0ZgBGAAAAAACxCzc14g3eSoadAxaGpB3ABwCr5qkyxHH4QY9vHKr6u5IrAAAAAAENAACr5qkyxHH4QY9vHKr6u5IrAARi2BmGAAC=', '$2y$10$fq6jvoNL/RShVKfNDy64EOGW0gLzd0GvfS.di16Z9LcCK7DpIHONK')
=> true

  

Исходя из моего понимания того, что делает хеширование, это не должно быть возможным, но, похоже, это не коллизия, поскольку замена B на C также приводит true .

Я использую Laravel 8.0 с PHP 7.4.11.

Есть идеи, что я здесь делаю не так?

ОБНОВЛЕНИЕ: нашел эту подсказку в официальной документации PHP для password_hash :

Внимание: Использование PASSWORD_BCRYPT в качестве алгоритма приведет к усечению параметра пароля до максимальной длины 72 символов.

Затем я проверил это, и действительно, изменение любого из символов позади AAMkAGEzN2EyZTg4LWRiNTUtNGIwYS04ZTA1LWE2Y2U5OTRjYjQ0ZgBGAAAAAACxCzc14g3e не меняет результат при замене, например, последнего e на f returns false for Hash::check() . Длина строки — это 72 символы, поэтому это может быть результатом усечения. Но почему?Это не упоминается в Hash документации Laravel. У меня есть несколько паролей длиной более 72 символов, так что на самом деле не имеет значения, как они заканчиваются?

В результате мне нужно использовать другую функцию Laravel для хэширования более длинных сообщений? Какое из них?

Ответ №1:

Я провел некоторое исследование и пришел к такому выводу:

В официальных документах laravel они ссылаются на официальные документы php. Здесь у них есть caution раздел, в password котором говорится:

Использование PASSWORD_BCRYPT в качестве алгоритма приведет к тому, что параметр пароля будет усечен до максимальной длины 72 символов.

Я проверил вашу строку, и она имеет длину 152. Поэтому в вашем случае вам следует использовать другой алгоритм (вы можете установить это в config/hashing.php at driver ) или убедиться, что входная строка содержит не более 72 символов.

Комментарии:

1. Спасибо. Это согласуется с моими выводами, которые я добавил в вопросе. Знаете ли вы, в чем причина такого решения?

2. Я немного огляделся, и я не могу найти, почему это 72. Но в целом существуют ограничения для ограничения функциональности, чтобы это не занимало много времени или не переполняло память.

3. Для меня это имеет смысл для password_hash функции, но не для Hash фасада, поскольку хэш-функции используются не только для хеширования паролей. По крайней мере, намек в документации был бы хорош, иначе произойдет много столкновений. Я добавил примечание для этого в Laravel docs GitHub , надеюсь, оно будет извлечено и поможет другим разработчикам не тратить столько времени на отладку … : D

4. Я имею в виду, что документы laravel ссылаются на документы php. Таким образом, они приводят вас к правильной информации, вам просто нужно самостоятельно найти информацию, как и в большинстве документов по программированию. Если в каждом документе нужно перечислять каждое требование, это станет адом для обслуживания, поскольку каждый должен обновлять одно и то же изменение в своих документах.

5. Смотрите первую часть этого ответа , почему существует эта проблема. @Johannes