【问题标题】:Why does crypt/blowfish generate the same hash with two different salts?为什么 crypt/blowfish 使用两种不同的盐生成相同的哈希?
【发布时间】:2011-01-14 14:33:00
【问题描述】:

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

当使用长度超过 22 个字符的 salt 字符串时,生成的哈希值没有变化(即截断),而当使用长度小于 21 个字符的字符串时,salt 将自动被填充(显然是 '$' 字符);这相当简单。但是,如果给定一个盐 20 个字符和一个盐 21 个字符,其中除了 21 长度盐的最后一个字符之外,两者相同,则两个散列字符串将相同。一个长度为 22 个字符的 salt,除了最后一个字符外,与 21 个长度的 salt 相同,hash 将再次不同。

代码示例:

$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 中,offset (28, 4) 为da$.,而在salt_21 中,offset (28, 4) 为da2.;但是,重要的是要注意生成的字符串包括哈希、盐以及生成盐的指令(即$2a$07$);实际上,发生差异的部分仍然是盐。实际哈希值不变为UxGYN739wLkV5PGoR1XA4EvNVPjwylG

因此,这实际上不是产生的哈希值的差异,而是用于存储哈希值的盐的差异,这正是手头的问题:两个盐生成相同的哈希值。

记住:输出将采用以下格式:

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

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

编辑 2:

在 cmets 中,要求我发布一些附加信息,因为用户无法重现我的输出。执行以下代码:

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)

希望这会有所帮助。

【问题讨论】:

  • 如果您快速浏览一下,它们看起来相同,但有 1 个字符差异
  • @Craig:实际上,那部分实际上仍然是哈希的盐,而不是哈希本身。哈希只是最后 32 个字符。
  • 嗯好吧,对不起,让我看起来很愚蠢:D

标签: php hash salt blowfish crypt


【解决方案1】:

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

【讨论】:

  • 其实那部分其实还是哈希的盐,而不是哈希本身。哈希只是最后 32 个字符。我已经编辑了这个问题,以反映盐被添加到字符串中的事实,以使问题更清楚。
  • 也许您可以发布一些有关您的 php 版本、主机操作系统以及以下 php 常量的值的信息: - CRYPT_SALT_LENGTH - CRYPT_STD_DES - CRYPT_EXT_DES - CRYPT_MD5 - CRYPT_BLOWFISH 当我运行您发布的代码时,我得到以下:string(13) "$2yymq4BO0Z3w" string(13) "$2yymq4BO0Z3w" string(13) "$2yymq4BO0Z3w" string(13) "$2yymq4BO0Z3w"
  • 您正在使用 STD_DES 进行加密 - 请注意如何只考虑给定盐的前两个字符。直到 PHP 5.3 才添加对 Blowfish 的内部支持(如果您的主机操作系统不支持),因此如果您有旧版本,它将恢复为默认方法,在您的设置中显示为 STD_DES。请参阅php.net/manual/en/function.crypt.php 了解更多信息。
  • 我敢冒险,crypt 函数有一些内部机制可以在接收到 salt 后对其进行操作,或者是一些扩展或收缩函数,以使其大小适合给定的加密算法(也许处理它作为base64,并且在解码时,如果它没有正确的字节数来读取它,不管最后几个字节如何,都会填充它)。我没有花时间去挖掘最新的 php 源代码来查看这个,但如果你真的很好奇,那你应该从那里开始!
【解决方案2】:

经过一些实验,我得出的结论是,这是由于盐的处理方式造成的。 salt 不被认为是文字文本,而是一个 base64 编码的字符串,这样 22 字节的 salt 数据实际上将代表一个 16 字节的 salt 字符串 (floor(22 * 24 / 32) == 16)。 “明白了!”不过,这种实现方式与 Unix crypt 一样,它使用“非标准”base64 字母表。确切地说,它使用这个字母表:

./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$

第 65 个字符“$”是填充字符。

现在,crypt() 函数似乎能够采用任何长度小于或等于其最大值的盐,并通过丢弃不构成另一个完整字节的任何数据来静默处理 base64 中的任何不一致.如果你在 salt 中传递不属于其 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 代码。盐不断以非随机方式旋转(基于当前时间到微秒的漩涡散列的随机片段),以及要散列的数据(这里称为$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

结论?双重。首先,它按预期工作,其次,知道自己的盐或不要自己滚盐。

【讨论】:

  • 确实如此。如果它使用标准的 base64 字母表可能会更明显,但是,好吧,生活还在继续。
【解决方案3】:

很好的答案,清晰的解释。但在我看来,要么实现中存在错误,要么需要进一步解释意图{帖子的 cmets 解释了为什么没有错误}。 current php documentation 声明:

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

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

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

但正如Dereleased 的回复所示,如果输入盐字符串有效,则输出由输入盐组成,该盐被填充为固定长度并带有“$”字符,并附加了 32 个字符的计算哈希值给它。不幸的是,结果中的盐只填充了 21 个 base64 数字,而不是 22!回复中的最后三行显示了这一点,我们看到一个 '$' 表示 20 位数字,没有 '$' 表示 21 位,当 salt 中有 22 个 base64 位时,哈希结果的第一个字符替换输入盐的第 22 位。该函数仍然可用,因为它计算的完整值作为substr(crypt($pw,$salt), 28, 32) 可供调用者使用,并且调用者已经知道完整的盐值,因为它将该字符串作为参数传递。但是很难理解为什么返回值被设计成只能给你 128 位盐值的 126 位。事实上,很难理解为什么它包含输入盐。但是省略2位实在是深不可测。

这里有一个小 sn-p,显示第 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 位盐的问题似乎也不会出现。 {正如帖子中的 cmets 所解释的,即使第 22 位数字被覆盖,覆盖它的哈希数字也只是 [.Oeu] 四个可能值之一,这些是第 22 位数字的唯一有效值。如果第 22 位不是这四个值之一,它将被这四个产生相同哈希值的一个替换。}

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

【讨论】:

  • 我完全同意你关于包含最后一个字符代表的部分字节值的观点。此外,我感谢您对这个有趣的跟进,基本上是水平扩展,它说明了与我的垂直扩展相同的另一个特质。但是,将盐附加到字符串的原因实际上相当简单:crypt(password, crypt(password, salt)) == crypt(password, salt)。此外,对于某些机制,应该有一种方法可以触发盐的自动生成,因此这是获得它的唯一方法。窃取你的字母表。
  • 啊,现在返回值的格式有意义了,谢谢!我正要说,如果你真的使用 22 位的盐,那么方程就被打破了,但我发现情况并非如此。虽然计算出的哈希值的第一个数字覆盖了输入盐的第 22 个数字,但是当第 22 个数字是 [.Oeu] 之一时,该数字也是哈希的第一个数字!这些 base64 数字的值分别为 0、16、32、48 - 高位两位。其他四位被忽略,因为二进制盐是 128 位,而 22 位将是 132 位。
  • 因此,当第 22 位不是 [.Oeu] 之一时,散列将其替换为这四个之一,并且等式仍然有效!再次检查时,我注意到我的帖子数据中存在剪切和粘贴问题,所以我会修复它并添加一些关于此的内容。再次感谢!
【解决方案4】:

根据我的调查,盐似乎总是 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() 的输出,则无需了解它们(我假设您的意思是将它们单独存储到哈希中)。

【讨论】:

    猜你喜欢
    • 2012-08-13
    • 2015-11-14
    • 1970-01-01
    • 2015-06-12
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 2017-11-15
    相关资源
    最近更新 更多