为什么crypt / blowfish使用两种不同的盐生成相同的哈希?

时间:2010-02-08 23:39:56

标签: php hash salt blowfish crypt

这个问题与PHP crypt()的实现有关。对于这个问题,盐的前7个字符不计算,因此盐“$2a$07$a”的长度为1,因为它只有1个字符的盐和7个字符的元数据

当使用长度超过22个字符的salt字符串时,生成的哈希值没有变化(即截断),当使用短于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

其中##是log-base-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)

希望这有帮助。

4 个答案:

答案 0 :(得分:35)

经过一些实验,我得出结论,这是由于盐的处理方式。 salt不被认为是文本文本,而是作为base64编码的字符串,这样22个字节的salt数据实际上代表一个16字节的字符串(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.但是,此附加字符仅代表6位数据,因此无法转换为另一个完整字节,因此它是丢弃。

因此,对于任何两种盐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()用来计算哈希值的实际盐实际上是相同的。作为证据,我将包含一些可用于显示此示例的PHP代码示例。盐不断地以非随机方式旋转(基于当前时间到微秒的漩涡散列的随机片段)和要散列的数据(此处称为{{1} })只是当前的Unix-Epoch时间。

$seed

这会产生类似于以下的输出

$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
    );
}

结论?双重。首先,它按预期工作,其次,了解自己的盐或不自己撒盐。

答案 1 :(得分:7)

很好的答案和明确的解释。但在我看来,实现中存在一个错误,或者需要对意图进行一些进一步的解释{对帖子的评论解释了为什么没有错误}。 current php documentation州:

  

CRYPT_BLOWFISH - 用下面的盐对河豚进行散列:“$ 2a $”,两位数的成本参数“$”,以及字母“./0-9A-Za-z”中的22位64位数字。在salt中使用此范围之外的字符将导致crypt()返回零长度字符串。两位数的成本参数是基于Blowfish的哈希算法的迭代计数的基数2对数,并且必须在04-31范围内,超出此范围的值将导致crypt()失败。

这与此处陈述和演示的内容一致。不幸的是,文档没有非常有用地描述返回值:

  

返回散列字符串或短于13个字符的字符串,并保证与失败时的盐不同。

但是如Dereleased的回复所示,如果输入salt字符串有效,则输出由输入盐填充到固定长度,带有'$'字符,带有32个字符的计算哈希值附加到它的值。不幸的是,结果中的盐被填充到只有21个base64数字,而不是22个!这个回复的最后三行显示了这一点,我们看到一个'$'代表20位数字,没有'$'代表21,当盐中有22个base64数字时,哈希结果的第一个字符取代了输入盐的第22位数字。该函数仍然可用,因为它计算的完整值对调用者可用substr(crypt($pw,$salt), 28, 32),并且调用者已经知道完整的salt值,因为它将该字符串作为参数传递。但是很难理解为什么设计返回值使得它只能给出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)

作为最后一点,虽然说明为什么哈希值在指定mod 4 == 1的base64数字的数量重复时,为什么代码可能会以这种方式表现,但它没有解释为什么编写代码这样做是个好主意。代码可以并且可以说应该包括来自base64数字的位,这些位在计算散列时构成部分字节,而不是仅丢弃它们。如果代码是以这种方式编写的,那么输出中丢失第22位盐的问题似乎也不会出现。 {正如对帖子的评论所解释的那样,即使第22位被覆盖,覆盖它的哈希数字也只是四个可能值中的一个[.Oeu],这些是第22个唯一的重要值。数字。如果第22个数字不是这四个数值中的一个,它将被产生相同散列的四个数字中的一个替换。}

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

答案 2 :(得分:3)

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

答案 3 :(得分:0)

根据我的调查,似乎盐总是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部分只存储有效位,因此它可能并不总是与输入盐匹配。好处是在验证时可以不加改变地使用哈希作为盐。因此,最好只存储crypt()返回的完整哈希,而不是最初使用的输入盐。实际上:

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

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

滚动你自己的盐不是问题,如果你按原样存储crypt()的输出,那么知道它们(通过这个,我认为你的意思是将它们分别存储到哈希)是没有必要的。