回答得很好,解释得很清楚。但在我看来,要么是实现中存在缺陷,要么需要对意图作进一步解释{帖子的评论解释了为什么没有缺陷}。这个
current php documentation
国家:
返回哈希字符串或短于13个字符的字符串,并保证在失败时与salt不同。
但正如我在答覆中所说:
Dereleased
substr(crypt($pw,$salt), 28, 32)
,调用方已经知道完整的salt值,因为它将该字符串作为参数传递。但是很难理解为什么返回值被设计成只能给出128位salt值中的126位。事实上,很难理解为什么它包含输入盐;但是省略其中的两个部分确实是难以理解的。
这里有一个小片段显示,第22个base64位只为计算中实际使用的salt增加了两位(只生成了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位的位,而不是丢弃它们。如果代码是这样写的,那么输出中丢失salt的第22位数字的问题似乎也不会出现{正如博文的评论所解释的,即使第22位数字被覆盖,覆盖它的散列数字也只是四个可能值中的一个
[.Oeu]
,这些是第22位的唯一有效值。如果第22位不是这四个值中的一个,它将被生成相同哈希的四个值中的一个替换。}
从评论来看,似乎没有bug,只是令人难以置信的沉默的文档:-)因为我不是一个密码学家,我不能以任何权威说这句话,但在我看来,这是算法的一个弱点,一个21位的salt显然可以产生所有可能的散列值,而22位salt将散列的第一位限制为四个值中的一个。