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
。
因此,实际上这不是产生的哈希值有所不同,而是用于存储哈希值的盐有所不同,这正是问题所在:两个盐生成相同的哈希值。
记住:输出将采用以下格式:
"$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)
希望这有所帮助。