这个问题与 PHP 的实现有关crypt() http://php.net/manual/en/function.crypt.php。对于这个问题,salt的前7个字符不算在内,所以一个salt '$2a$07$a
' 的长度被认为是 1,因为它只有 1 个盐字符和 7 个元数据字符。
当使用长度超过 22 个字符的 salt 字符串时,生成的哈希值不会发生变化(即截断),并且当使用短于 21 个字符的字符串时,salt 将自动填充(用 '$
' 字符,显然);这相当简单。但是,如果给定一个 salt 20 个字符和一个 salt 21 个字符,其中除了 21 长度 salt 的最后一个字符之外,两者相同,则两个哈希字符串将相同。盐长度为 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"
为什么是这样?
EDIT:
一些用户注意到整个字符串存在差异,这是事实。在salt_20
,偏移量 (28, 4) 为da$.
, 而在salt_21
,偏移量 (28, 4) 为da2.
;然而,值得注意的是,生成的字符串包括哈希、盐以及生成盐的指令(即$2a$07$
);事实上,发生差异的部分仍然是盐。实际的哈希值不变UxGYN739wLkV5PGoR1XA4EvNVPjwylG
.
因此,这实际上不是产生的哈希值的差异,而是用于存储哈希值的盐的差异,这正是当前的问题:两个盐生成相同的哈希值。
请记住:输出将采用以下格式:
"$2a$##$saltsaltsaltsaltsaltsaHASHhashHASHhashHASHhashHASHhash"
// ^ Hash Starts Here, offset 28,32
其中 ## 是 log-base-2,确定算法运行的迭代次数
Edit 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)
希望这可以帮助。