Почему crypt / blowfish генерируют один и тот же хэш с двумя разными солями?

Этот вопрос связан с реализацией PHP crypt() . По этому вопросу первые 7 символов соли не учитываются, поэтому соль « $2a$07$a » будет иметь длину 1, так как это всего лишь 1 символ соли и семь символов метаданных ,

При использовании строк солей длиной более 22 символов изменение хэша не генерируется (т. Е. Усечение), а при использовании строк длиной менее 21 символа соль будет автоматически дополняться (с символами ' $ ', по-видимому); это довольно просто. Однако, если задана соль 20 символов и соль 21 символа, где эти два идентичны, за исключением окончательного символа соли длиной 21 длины, обе хешированные строки будут одинаковыми. Соль длиной 22 символа, которая идентична соли длины 21, за исключением конечного символа, хэш снова будет другим.

Пример в коде:

 $foo = 'bar'; $salt_xx = '$2a$07$'; $salt_19 = $salt_xx . 'b1b2ee48991281a439d'; $salt_20 = $salt_19 . 'a'; $salt_21 = $salt_20 . '2'; $salt_22 = $salt_21 . 'b'; var_dump( crypt($foo, $salt_19), crypt($foo, $salt_20), crypt($foo, $salt_21), crypt($foo, $salt_22) ); 

Будет производить:

 string(60) "$2a$07$b1b2ee48991281a439d$$.dEUdhUoQXVqUieLTCp0cFVolhFcbuNi" string(60) "$2a$07$b1b2ee48991281a439da$.UxGYN739wLkV5PGoR1XA4EvNVPjwylG" string(60) "$2a$07$b1b2ee48991281a439da2.UxGYN739wLkV5PGoR1XA4EvNVPjwylG" string(60) "$2a$07$b1b2ee48991281a439da2O4AH0.y/AsOuzMpI.f4sBs8E2hQjPUQq" 

Почему это?

РЕДАКТИРОВАТЬ:

Некоторые пользователи отмечают, что существует разница в общей строке, что верно. В salt_20 смещение (28, 4) – da$. , а в salt_21 смещение (28, 4) – da2. ; однако важно отметить, что генерируемая строка включает хеш, соль, а также инструкции для генерации соли (т.е. $2a$07$ ); часть, в которой происходит различие, по-прежнему остается солью. Фактический хэш не изменяется как UxGYN739wLkV5PGoR1XA4EvNVPjwylG .

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

Rembmer: выход будет в следующем формате:

 "$2a$##$saltsaltsaltsaltsaltsaHASHhashHASHhashHASHhashHASHhash" // ^ Hash Starts Here, offset 28,32 

где ## – это лог-база-2, определяющая количество итераций, выполняемых алгоритмом для

Изменить 2:

В комментариях было предложено добавить дополнительную информацию, так как пользователь не смог воспроизвести мой вывод. Выполнение следующего кода:

 var_dump( PHP_VERSION, PHP_OS, CRYPT_SALT_LENGTH, CRYPT_STD_DES, CRYPT_EXT_DES, CRYPT_MD5, CRYPT_BLOWFISH ); 

Производит следующий вывод:

 string(5) "5.3.0" string(5) "WINNT" int(60) int(1) int(1) int(1) int(1) 

Надеюсь это поможет.

После некоторых экспериментов я пришел к выводу, что это связано с тем, как обрабатывается соль. Соль не считается литеральным текстом, а скорее представляет собой кодированную в base64 строку, так что 22 байта данных соли фактически представляют собой 16-байтовую строку (соль floor(22 * 24 / 32) == 16 ) соли. «Готча!» с этой реализацией, однако, заключается в том, что, подобно склепу Unix, он использует «нестандартный» алфавит base64. Если быть точным, он использует этот алфавит:

 ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$ 

65-й ​​символ, ' $ ', является дополняющим символом.

Теперь функция crypt() по-видимому, способна принимать соль любой длины, меньшей или равной ее максимуму, и молча обращаться с любыми несогласованностями в базе64, отбрасывая любые данные, которые не составляют другой полный байт. Функция склепа полностью сработает, если вы передадите ей символы в соли, которые не являются частью его алфавита base64, что просто подтверждает эту теорию его работы.

Возьмите воображаемую соль « 1234 ». Это идеально соответствует base64, поскольку он представляет 24 бита данных, поэтому 3 байта, и не несет никаких данных, которые необходимо отбросить. Это соль, у которой Len Mod 4 равен нулю. Добавляйте любой символ к этой соли, и он становится солью из 5 символов, а Len Mod 4 теперь 1. Однако этот дополнительный символ представляет только шесть бит данных и поэтому не может быть преобразован в другой полный байт, поэтому он отбрасывается.

Таким образом, для любых двух солей А и В, где

  Len A Mod 4 == 0 && Len B Mod 4 == 1 // these two lines mean the same thing && Len B = Len A + 1 // but are semantically important separately && A == substr B, 0, Len A 

Фактическая соль, используемая crypt() для вычисления хэша, фактически будет идентичной. В качестве доказательства я включаю пример кода PHP, который можно использовать для показа этого. Соль постоянно вращается полу -неслучайным образом (на основе рандомизированного сегмента хэша вихревого потока текущего времени до микросекунды), а данные, которые нужно хэшировать (здесь и называется $seed ), – это просто текущее время Unix-Epoch ,

 $salt = substr(hash('whirlpool',microtime()),rand(0,105),22); $seed = time(); for ($i = 0, $j = strlen($salt); $i <= $j; ++$i) { printf('%02d = %s%s%c', $i, crypt($seed,'$2a$07$' . substr($salt, 0, $i)), $i%4 == 0 || $i % 4 == 1 ? ' <-' : '', 0x0A ); } 

И это дает результат, аналогичный следующему

 00 = $2a$07$$$$$$$$$$$$$$$$$$$$$$.rBxL4x0LvuUp8rhGfnEKSOevBKB5V2. <- 01 = $2a$07$e$$$$$$$$$$$$$$$$$$$$.rBxL4x0LvuUp8rhGfnEKSOevBKB5V2. <- 02 = $2a$07$e8$$$$$$$$$$$$$$$$$$$.WEimjvvOvQ.lGh/V6HFkts7Rq5rpXZG 03 = $2a$07$e89$$$$$$$$$$$$$$$$$$.Ww5p352lsfQCWarRIWWGGbKa074K4/. 04 = $2a$07$e895$$$$$$$$$$$$$$$$$.ZGSPawtL.pOeNI74nhhnHowYrJBrLuW <- 05 = $2a$07$e8955$$$$$$$$$$$$$$$$.ZGSPawtL.pOeNI74nhhnHowYrJBrLuW <- 06 = $2a$07$e8955b$$$$$$$$$$$$$$$.2UumGVfyc4SgAZBs5P6IKlUYma7sxqa 07 = $2a$07$e8955be$$$$$$$$$$$$$$.gb6deOAckxHP/WIZOGPZ6/P3oUSQkPm 08 = $2a$07$e8955be6$$$$$$$$$$$$$.5gox0YOqQMfF6FBU9weAz5RmcIKZoki <- 09 = $2a$07$e8955be61$$$$$$$$$$$$.5gox0YOqQMfF6FBU9weAz5RmcIKZoki <- 10 = $2a$07$e8955be616$$$$$$$$$$$.hWHhdkS9Z3m7/PMKn1Ko7Qf2S7H4ttK 11 = $2a$07$e8955be6162$$$$$$$$$$.meHPOa25CYG2G8JrbC8dPQuWf9yw0Iy 12 = $2a$07$e8955be61624$$$$$$$$$.vcp/UGtAwLJWvtKTndM7w1/30NuYdYa <- 13 = $2a$07$e8955be616246$$$$$$$$.vcp/UGtAwLJWvtKTndM7w1/30NuYdYa <- 14 = $2a$07$e8955be6162468$$$$$$$.OTzcPMwrtXxx6YHKtaX0mypWvqJK5Ye 15 = $2a$07$e8955be6162468d$$$$$$.pDcOFp68WnHqU8tZJxuf2V0nqUqwc0W 16 = $2a$07$e8955be6162468de$$$$$.YDv5tkOeXkOECJmjl1R8zXVRMlU0rJi <- 17 = $2a$07$e8955be6162468deb$$$$.YDv5tkOeXkOECJmjl1R8zXVRMlU0rJi <- 18 = $2a$07$e8955be6162468deb0$$$.aNZIHogUlCn8H7W3naR50pzEsQgnakq 19 = $2a$07$e8955be6162468deb0d$$.ytfAwRL.czZr/K3hGPmbgJlheoZUyL2 20 = $2a$07$e8955be6162468deb0da$.0xhS8VgxJOn4skeI02VNI6jI6324EPe <- 21 = $2a$07$e8955be6162468deb0da3.0xhS8VgxJOn4skeI02VNI6jI6324EPe <- 22 = $2a$07$e8955be6162468deb0da3ucYVpET7X/5YddEeJxVqqUIxs3COrdym 

Вывод? Двукратное. Во-первых, он работает по назначению, а во-вторых, знает свою соль или не откатывает свою соль.

Отличный ответ и ясное объяснение. Но мне кажется, что есть либо ошибка в реализации, либо какое-то дополнительное объяснение намерения (комментарии к сообщению объясняют, почему нет ошибки). В текущей документации php указано:

CRYPT_BLOWFISH – хеширование Blowfish с солью следующим образом: «$ 2a $», двухзначный параметр стоимости, «$» и 22 базовых 64 цифры из алфавита «./0-9A-Za-z». Использование символов вне этого диапазона в соли приведет к тому, что crypt () вернет строку нулевой длины. Параметр стоимости двух цифр является логарифмом базы-2 итерации для базового алгоритма хэширования на основе Blowfish и должен находиться в диапазоне 04-31, значения вне этого диапазона вызовут сбой crypt ().

Это согласуется с тем, что было сказано и показано здесь. К сожалению, документация не очень хорошо описывает возвращаемое значение:

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

Но, как показано в ответе Dereleased , если строка входных солей действительна, вывод состоит из входной соли, заполненной до фиксированной длины символами $ $, с добавленным к ней 32-символьным вычисленным значением хэша. К сожалению, соль в результате дополняется только 21 base64 цифрами, а не 22! Это показано последними тремя строками в этом ответе, где мы видим один «$» для 20 цифр, не «$» для 21, а когда в соли 22 знака base64, первый символ хеш-результата заменяет 22-я цифра входной соли. Функция все еще пригодна для использования, поскольку полное значение, которое он вычисляет, доступно для вызывающего в качестве substr(crypt($pw,$salt), 28, 32) , и вызывающий абонент уже знает полное значение соли, поскольку он передал эту строку как аргумент. Но очень сложно понять, почему возвращаемое значение сконструировано так, что оно может дать вам только 126 бит 128-битного значения соли. На самом деле, трудно понять, почему он включает соль для ввода вообще; но пропуская 2 бита, это действительно непостижимо.

Вот небольшой фрагмент, показывающий, что 22-я base64-цифра вносит только два бита в соль, фактически используемую при вычислении (есть только 4 отдельных хэша):

 $alphabet = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $lim = strlen($alphabet); $saltprefix = '$2a$04$123456789012345678901'; // 21 base64 digits for ($i = 0; $i < $lim; ++$i ) { if ($i = 16 || $i == 32 || $i == 48) echo "\n"; $salt = $saltprefix . substr($alphabet, $i, 1); $crypt = crypt($password, $salt); echo "salt ='$salt'\ncrypt='$crypt'\n"; } salt ='$2a$04$123456789012345678901.' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901/' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901A' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901B' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901C' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901D' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901E' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901F' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901G' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901H' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901I' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901J' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901K' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901L' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901M' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901N' crypt='$2a$04$123456789012345678901.YpaB4l25IJ3b3F3H8trjHXj5SC1UbUW' salt ='$2a$04$123456789012345678901O' crypt='$2a$04$123456789012345678901Ots44xXtSV0f6zMrHerQ2IANdsJ.2ioG' salty='$2a$04$123456789012345678901P' crypt='$2a$04$123456789012345678901Ots44xXtSV0f6zMrHerQ2IANdsJ.2ioG' salty='$2a$04$123456789012345678901Q' crypt='$2a$04$123456789012345678901Ots44xXtSV0f6zMrHerQ2IANdsJ.2ioG' ... 13 more pairs of output lines with same hash salt ='$2a$04$123456789012345678901e' crypt='$2a$04$123456789012345678901e.1cixwQ2qnBqwFeEcMfNfXApRK0ktqm' ... 15 more pairs of output lines with same hash salt ='$2a$04$123456789012345678901u' crypt='$2a$04$123456789012345678901u5yLyHIE2JetWU67zG7qvtusQ2KIZhAa' ... 15 more pairs of output lines with same hash 

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

Возможно, интерфейс был разработан таким образом для некоторой совместимости, и, возможно, потому, что он уже отправлен таким образом, его нельзя изменить. {первый комментарий к сообщению объясняет, почему интерфейс такой). Но, конечно, документация должна объяснить, что происходит. На всякий случай ошибка может быть исправлена ​​однажды, возможно, было бы безопаснее получить хэш-значение с помощью:

 substr(crypt($pw,$salt), -32) 

В заключение, хотя объяснение того, почему хеш-значение повторяется, когда количество цифр base64, указанных mod 4 == 1 имеет смысл с точки зрения того, почему код может вести себя таким образом, он не объясняет, почему писать код таким образом хорошая идея. Код может и, возможно, должен включать в себя биты из знака base64, который составляет частичный байт при вычислении хеша, вместо того, чтобы просто отбрасывать их. Если бы код был написан таким образом, то, скорее всего, проблема с потерей 22-й цифры соли на выходе тоже не появлялась. {Поскольку комментарии к сообщению объясняют, даже несмотря на то, что 22-я цифра перезаписана, цифра хэша, которая ее перезаписывает, будет только одним из четырех возможных значений [.Oeu] , и это единственные значимые значения для 22-й цифры , Если 22-я цифра не является одним из этих четырех значений, она будет заменена одной из тех четырех, которые выдают один и тот же хэш.}

В свете комментариев, кажется, что нет ошибки, просто невероятно молчаливая документация 🙂 Поскольку я не криптограф, я не могу сказать это ни с какими полномочиями, но мне кажется, что это слабость что 21-значная соль, по-видимому, может создавать все возможные значения хэширования, тогда как 22-значная соль ограничивает первую цифру хэша только одним из четырех значений.

Похоже, что результаты на самом деле разные. (da $, vs da2) для результата salt_20 и salt_21.

Из моего исследования казалось, что соль всегда 22 символа, а смещение хэша – 29, а не 28, что составляет 31 символ в длину, а не 32. Я запустил этот код:

 $pass = 'foobarbazqux'; $salt = 'cmfh./TCmc3m0X.MnmHGO'; $cost = 8; $crypt_salt = sprintf('$2a$%02d$%s', $cost, $salt); $chars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; for ($i = 0; $i < strlen($chars); $i++) { $hash = crypt($pass, $crypt_salt . $chars[$i]); var_dump($crypt_salt . $chars[$i], $hash, crypt($pass, $hash)); } 

Результаты:

 string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO." string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO/" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO0" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO1" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO2" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO3" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO4" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO5" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO6" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO7" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO8" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGO9" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOA" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOB" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOC" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOD" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOE" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOF" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOH" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOI" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOJ" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOL" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOM" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGON" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGO.t0NzWGmKpRimP4RhjFMg3F020kVKG9S" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOO" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOP" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOQ" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOR" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOS" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOT" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOU" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOV" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOW" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOX" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOY" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOZ" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOa" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOb" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOc" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOd" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOOSYI2wLIE3NElcU7itPPQnj8iW922mwy" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOe" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOf" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOg" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOh" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOi" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOj" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOk" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOl" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOm" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOn" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOo" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOp" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOq" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOr" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOs" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOt" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOeLcyQf2JnDryc7eA43zx3qi1uJKZUtPK" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOu" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOv" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOw" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOx" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOy" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(29) "$2a$08$cmfh./TCmc3m0X.MnmHGOz" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" string(60) "$2a$08$cmfh./TCmc3m0X.MnmHGOutgqolF/BikhkbIM1yMA7HQpkbDxULoG" 

Это говорит о том, что солевая часть возвращенного хэша хранит только значимые биты, поэтому он может не всегда соответствовать вашей входной соли. Преимущество состоит в том, что хеш можно использовать без изменения в качестве соли при проверке. Таким образом, вам лучше сохранить только хэш, возвращаемый crypt() , и никогда не использовать входную соль, которую вы используете изначально. Практически:

 $hash_to_store = crypt($new_password, $formatted_salt); 

а также

 $verified = $stored_hash == crypt($entered_password, $stored_hash); 

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