为什么crypt / blowfish与两种不同的盐产生相同的散列?

这个问题与PHP的crypt()的实现有关。 对于这个问题,盐的前7个字符不计算在内,所以盐' $2a$07$a '将被认为长度为1,因为盐只有1个字符,元数据有7个字符。

当使用超过22个字符的saltstring时,生成的哈希(即截断)没有变化,并且当使用less于21个字符的string时,salt将自动填充(显然为' $ '字符)。 这是相当简单的。 但是,如果给定一个盐20个字符和一个盐21个字符,除了21长度盐的最后一个字符,两个字符是相同的,那么两个哈希string将是相同的。 一个长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" 

为什么是这样?

编辑:

有些用户注意到整个string有差别,这是真的。 在salt_20 ,偏移( salt_20 )是da$. 而在salt_21 ,偏移( salt_21 )是da2. ; 然而,重要的是要注意,生成的string包括哈希,盐,以及生成盐的说明(即$2a$07$ ); 事实上,发生差异的部分仍然是盐。 实际的散列不变,因为UxGYN739wLkV5PGoR1XA4EvNVPjwylG

因此,这实际上并不是所产生的散列的差异,而是用于存储散列的盐的差别,这正好是手头的问题:两种盐产生相同的散列。

Rembmer:输出将采用以下格式:

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

其中##是log-base-2,确定algorithm运行的迭代次数

编辑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编码的string,使得22个字节的盐数据实际上将代表盐的16字节string( floor(22 * 24 / 32) == 16 )。 “Gotcha!” 但是,这个实现就像Unix crypt一样,它使用了一个“非标准的”base64字母表。 确切地说,它使用这个字母表:

 ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$ 

第65个字符' $ '是填充字符。

现在, crypt()函数似乎能够获取小于或等于其最大值的任何长度的salt,并通过丢弃任何不构成另一个完整字节的数据来静静地处理base64中的任何不一致。 如果你把那些不是它的base64字母表的一部分的字符传给它,crypt函数将会完全失败,这只是证实了它的操作理论。

拿一个假想的盐' 1234 '。 这是完全base64一致的,因为它表示24位数据,所以3个字节,并且不携带任何需要丢弃的数据。 这是Len Mod 4为零的盐。 追加任何字符到盐,它变成一个5字符的盐, Len Mod 4现在是1.然而,这个额外的字符只代表六位数据,因此不能被转换成另一个完整的字节,所以它被丢弃。

因此,对于任何两种盐A和B,其中

  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()用来计算散列的实际盐实际上是相同的。 作为certificate,我包含了一些可用于显示的示例PHP代码。 盐以非随机的方式不断地旋转(基于当前时间的漩涡散列的随机段到微秒),并且要被散列的数据(在此称为$seed )仅仅是当前的Unix时代时间。

 $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 $”,一个两位数的开销参数“$”,以及字母表“.0-9A-Za-z”中的22位64位数字。 在salt中使用此范围之外的字符将导致crypt()返回一个零长度的string。 两位数字代价参数是底层基于Blowfish的散列algorithm计算器的迭代计数的基数2对数,并且必须在04-31范围内,超出此范围的值将导致crypt()失败。

这与这里陈述和论证的内容是一致的。 不幸的是,这些文档没有很好地描述返回值:

返回散列string或长度小于13个字符的string,保证与失败时的salt不同。

但是,如Dereleased的回复所示,如果inputsaltstring是有效的,则输出包括用“$”字符填充到固定长度的inputsalt,并附加32个字符的计算后的散列值。 不幸的是,结果中的盐被填充到只有21个base64数字,而不是22! 这在回复中的最后三行显示,其中我们看到一个“$”表示20位数字,没有“$”表示21,并且当salt中有22个base64数字时,散列结果的第一个字符replaceinput盐的第22位数字。 该函数仍然可用,因为它计算的完整值可作为substr(crypt($pw,$salt), 28, 32) )提供给调用者,并且调用者已经知道完整的salt值,因为它将该string作为论据。 但是很难理解为什么返回值被devise成只能给你128位盐值的126位。 事实上,很难理解为什么它包括投入的盐; 但是省略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 

相同哈希值的分组还表明,实际使用的字母表的映射最有可能是写在这里,而不是按照另一个答复中显示的顺序。

也许接口是为了某种兼容性而devise的,也许是因为它已经以这种方式发货了,所以不能改变。 {这篇文章的第一条评论解释了为什么界面是这样的}。 但是,文件当然应该解释发生了什么事情。 为防万一这个bug有可能在一天之内得到修正,也许最安全的方法是用下面的方法获得散列值:

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

最后要说明的是,当指定mod 4 == 1的base64数字的数目为什么哈希值重复的解释对于为什么代码可能以这种方式行事而言是有意义的,这并不能解释为什么以这种方式编写代码是一个好主意。 代码可以并且可以理解地包含来自base64数字的位,它们在计算散列时构成部分字节,而不是丢弃它们。 如果代码是这样写的,那么在输出中丢失第22位数字的问题似乎也不会出现。 {正如对post的评论所解释的那样,即使第22位数字被覆盖,覆盖它的散列的数字也将只是四个可能值中的一个[.Oeu] ,并且这些是第22位的唯一有效值。 如果第二十二位数字不是这四个值中的一个,则它将被产生相同散列的那四个中的一个replace。}

根据评论,似乎很清楚没有错误,只是令人难以置信的默认文档:-)因为我不是一个密码学家,我不能说任何权威,但在我看来,这是一个弱点algorithm,21位数的盐显然可以产生所有可能的哈希值,而22位数的盐限制哈希的第一个数字仅为四个值中的一个。

看起来输出实际上是不同的。 (da $,vs da2)作为salt_20和salt_21的结果。

从我的调查来看,salt似乎总是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" 

这表明返回散列的salt部分只存储有效位,所以它可能并不总是与你input的salt匹配。 好处是在validation时可以不加改变地使用散列。 因此,你最好只存储由crypt()返回的完整散列,而不是最初使用的inputsalt。 实际上,

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

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

滚动你自己的盐不是一个问题,并且知道它们(通过这个,我假定你的意思是把它们分别存储到哈希中)是没有必要的,如果你正在存储crypt()的输出。