用于密码哈希的加密。河豚产生奇怪的输出

2022-08-30 21:58:00

我在理解php的crypt函数时遇到了一点麻烦。我的PHP版本是5.4.7。

我想使用crypt将加盐的密码存储在数据库中,因为据我所知,使用md5散列密码的开发人员将被当场押注和烧毁。

我想使用河豚alg来生成哈希值。现在,根据php文档,如果你用“$2y$”+成本(例如:“08”)+“$”+22个字符的盐(./0-9A-Za-z)来称呼它,crypt就会使用河豚。但是,这一小段测试代码的输出让我感到困惑:

echo "<pre>";
if (CRYPT_BLOWFISH == 1) {
    echo 'Blowfish SaltLen = 18:     ' . crypt('string that should be hashed', '$2y$08$123456789012345678') . "\n";
    echo 'Blowfish SaltLen = 19:     ' . crypt('string that should be hashed', '$2y$08$1234567890123456789') . "\n";
    echo 'Blowfish SaltLen = 20:     ' . crypt('string that should be hashed', '$2y$08$12345678901234567890') . "\n";
    echo 'Blowfish SaltLen = 21:     ' . crypt('string that should be hashed', '$2y$08$123456789012345678901') . "\n";
    echo 'Blowfish SaltLen = 22:     ' . crypt('string that should be hashed', '$2y$08$1234567890123456789012') . "\n";
}
echo "</pre>";

输出:

Blowfish SaltLen = 18:     $2y$08$123456789012345678$$$.Gq4WBozZb6XYmOJ88OC8gThSTUx8pRO
Blowfish SaltLen = 19:     $2y$08$1234567890123456789$$.u8Qm7Q9KVtvo2zwpKkN5ntAxu71k2pO
Blowfish SaltLen = 20:     $2y$08$12345678901234567890$.iIlIFEGaqDj6XbnKkK1F14HmMGLV.mu
Blowfish SaltLen = 21:     $2y$08$123456789012345678901.iIlIFEGaqDj6XbnKkK1F14HmMGLV.mu
Blowfish SaltLen = 22:     $2y$08$123456789012345678901uSFz9yPi/jA6e9aMcUkm7y.TJYhcCoSu

如果不够长,隐蔽的地下室会用$填充盐。盐会发生变化,哈希值也会发生变化。因此,第 1 行和第 2 行中的输出与预期一样。

然而,让我感到困惑的是最后三个哈希:显然,点将哈希与盐分开(?),但如果你真的给crypt提供它需要的22个字符作为盐,点就会消失。此外,盐的最后一个字符不会出现在输出中,但与 21 个字符的盐相比,哈希会发生变化。

特别令人困惑的是输出中的第3行和第4行!盐明显不同,但哈希值完全相同。我只是看不出这方面有任何一致性,非常感谢帮助。


答案 1

你看到问题的原因是它实际上并没有使用22个字符的盐。它仅使用 21.25 个字符。因此,第22个字符的几位用于盐,其余的用于哈希(结果)。

原因是盐不是一根绳子。这是一个 128 位数字。该数字将序列化为 base64。为了查看 base 64 的工作原理,每个 3 字节块被“转换”为一个 4 字节块。

[byte1][byte2][byte3]
[new1][new2][new3][new4]

现在,请记住,每个原始字节都有 8 位。因此,每个“新字节”只有6位(因为我们没有添加信息,我们只是以不同的方式表示它)。

所以发生的事情是,你只提供了21个字符的数据。解码时转换为15.75字节。但是你不能有部分字节。因此,最后一个解码的块被丢弃(由于信息不足)。我们扔掉的那6位完全映射到第21个字符。

因此,如果没有从第 22 个字符使用的 2 位,则必须丢弃第 21 位(因为部分字节没有意义)。

我们可以测试一下:

$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
for ($i = 0; $i < strlen($chars); $i++) {
    echo crypt('string that should be hashed', '$2y$08$12345678901234567890' . $chars[$i]) . "\n";
}

生产:

$2y$08$123456789012345678900.iIlIFEGaqDj6XbnKkK1F14HmMGLV.mu
$2y$08$123456789012345678901.iIlIFEGaqDj6XbnKkK1F14HmMGLV.mu
$2y$08$123456789012345678902.iIlIFEGaqDj6XbnKkK1F14HmMGLV.mu
.
.
.
$2y$08$12345678901234567890/.iIlIFEGaqDj6XbnKkK1F14HmMGLV.mu

但是,如果我们添加第22个字节(无论它是什么):

$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
for ($i = 0; $i < strlen($chars); $i++) {
    echo crypt('string that should be hashed', '$2y$08$12345678901234567890' . $chars[$i] . 'a') . "\n";
}

然后我们得到随机分布的效果:

$2y$08$123456789012345678900OtUUu.EAOjrOztGKf2m.TZIe7HGzFgF.
$2y$08$123456789012345678901Ou28wcnld1gB2vjW9obdQdz6kLMasqKC
$2y$08$123456789012345678902Oum7Yp/p4TEeEC5JxsmnQsACNnnK0cv2
$2y$08$123456789012345678903OxMer1AD.P.UpAMlykl5SokMmDM1BU0W
$2y$08$123456789012345678904OpoNDsh7DaAoSjiZFJKO7iMy53BqwsjO
$2y$08$123456789012345678905OQRUqlnlEpBzccxrCgyZVtl6a.tQxNz6
$2y$08$123456789012345678906O6QMFdYZ.tvQpSdYaxlFl1Rlsk05/Aym
$2y$08$123456789012345678907OwF1TKI.OYT3xtBxg8tqex4L8mZttUCm
$2y$08$123456789012345678908OtzJXaS8/x0KYQ2epPRgVSjWSy/yAwMK
$2y$08$123456789012345678909O17D/xQeJGLIzpwBZuN2kxdpxi6p3aDq
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890bOD9Z5cUlQgJtvhqSOIK/3BV/1QIEmHby
$2y$08$12345678901234567890cOG5DxIU4B/ftl01V/MhViyi8YymLKEdC
$2y$08$12345678901234567890dOcd0.C8PVpjqW7oGI9AZuTVjNwxZDDpa
$2y$08$12345678901234567890eOLQSg5zmHm2nOCmRMdNeY8LxW1xMKnwm
$2y$08$12345678901234567890fOI.DZa4KuxngvaBT8JFtRWY8oRs9A266
$2y$08$12345678901234567890gOTA9XsdwxujLBdLaypPHehWjj1GyjDRC
$2y$08$12345678901234567890hOkS/cZmSqtdHSWz3zPkImTfZbHvdC8Wm
$2y$08$12345678901234567890iOXDaVzn/h7/oQtUgHyPodyggGkOqxFdW
$2y$08$12345678901234567890jODbaT2pRSwnD2qHm43YdAbHVPBJ8iapi
$2y$08$12345678901234567890kOrUyng3J5OCChkP6tHiM.rz4o4CdPkTO
$2y$08$12345678901234567890lOtscWm7fnlUJXZIXLKhVI7E2Abh7uc3i
$2y$08$12345678901234567890mOCeJM40E/G0WrJ4utkSaJwtZUMCae326
$2y$08$12345678901234567890nO4ac8AzrsXk6HpAtOaGEvGfS8eceFtSC
$2y$08$12345678901234567890oOv3BFJmdPMx9josbfOHHtu/7xgoGUygq
$2y$08$12345678901234567890pOPWQlIGa.WBx8kDEEG05uWhUioyNqWiq
$2y$08$12345678901234567890qOg2ufL5bmYfAoZEFknsRaSOlI4GVBKWy
$2y$08$12345678901234567890rOJZTvmghag6zIY5Ha7iOCgArPZGotche
$2y$08$12345678901234567890sOZjZ2OaVZy.GeXp/BQvjbCpXgNa/GAlK
$2y$08$12345678901234567890tO3bAZAMEXEZm72/mAkbJkefUua9CUFuy
$2y$08$12345678901234567890uOQ.i2vydj6OGyl84Qhg5OXPq7OkRQomu
$2y$08$12345678901234567890vOc9BKZfLu6mcd2mIfLtmT6C6JwDT.Siq
$2y$08$12345678901234567890wO7ow2JgV.7yzEsllHUbhbMrOMKXSihsq
$2y$08$12345678901234567890xOUI89zc5eDCCCHoTljMyXuGXmIz9b0PW
$2y$08$12345678901234567890yORmKbjoeO.1HSpQB7L5EBMSRjJr4lR62
$2y$08$12345678901234567890zOZkhGY/cILtgQRmHLkx//nuzLXSwLqYy
$2y$08$12345678901234567890AOuJWX5/tdzRCTTs5EXYioLP1t7u1Ao7u
$2y$08$12345678901234567890BO2vHWuKdbL2lsbBQwaAkWCXz/YVEaHP2
$2y$08$12345678901234567890COedKIdK.eAjm2zF0CAnuM9XxbO3CakoK
$2y$08$12345678901234567890DOpunwAyx9X4/tJzDmUXARABluQdRV7Ji
$2y$08$12345678901234567890EOB1ONHz9lELb7iUvtzTi.PTSgN2tFv1.
$2y$08$12345678901234567890FOplAZBguPKXbAQDxq9PXqgjH/1ZX6u7C
$2y$08$12345678901234567890GOP/G3kfN/r92DIQlC0eVyGi3jWRUoVXK
$2y$08$12345678901234567890HOmala7V1QCL7PX79yODRg2Y5lTq6i/ii
$2y$08$12345678901234567890IOWbq1AXhTucizWIBn58rgVYFpRxMpm8.
$2y$08$12345678901234567890JOxgmM1XAcDg7AUpzeHzHxn6z75ljNoDy
$2y$08$12345678901234567890KOTnfd7pzmfzf80CrXxWC24sK3y1DAbb6
$2y$08$12345678901234567890LOXxQX37TiNlNMfZUtMLZFrZah8u39q9K
$2y$08$12345678901234567890MOmpvWu3ZKbbilLb4f8QF6OUPPpEbsM42
$2y$08$12345678901234567890NO8VjZ2KNbOVoOzgP/Tjd6IFtwjRG2PJ2
$2y$08$12345678901234567890OOvSnZoahC5g1Ewlm6K7US13i6vJIQSqm
$2y$08$12345678901234567890POVs5m/8eCyLd11zjEPYoYhpaZAz6PYF2
$2y$08$12345678901234567890QOk4MBZhDwzS8dwJl6lm.hdAVBcllSid2
$2y$08$12345678901234567890ROWh4H3TuKSuFfrtx1vqHnU/RrQ0HrbNW
$2y$08$12345678901234567890SOd/USMzVBx6wyPgsuvAszCIVZ6zOA44O
$2y$08$12345678901234567890TO53YobspFDSFshtGX9hH4LTw2OT2T4P.
$2y$08$12345678901234567890UOMLp7HSCxWMMxgJVN6JTN7WRKlRPN17y
$2y$08$12345678901234567890VOmOMGgpLXOV/mft8WXOWXmQjc71SN6g2
$2y$08$12345678901234567890WOiAkYTQmitOHabdScoZivJ4JeKtJ6t7.
$2y$08$12345678901234567890XOUUqRtGjd/nob.UiRrJvFyKSMELAIuZe
$2y$08$12345678901234567890YOukccL1Y2PDV9ErOLHileZOq5m6zIzSy
$2y$08$12345678901234567890ZOMNrfK..n1YjuP3F.S4Taxn0XvIf5gXW
$2y$08$12345678901234567890.OmG2XbJMpLDBrtq44ptVtXkVaGdAT9oO
$2y$08$12345678901234567890/OTN4hG/XcY.FtrT85TGI.Vm0sH0tpQ.a

现在,为了证明我们只使用最后一个字节中的几个位,让我们改变一下,保持第21个固定:

$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
for ($i = 0; $i < strlen($chars); $i++) {
    echo crypt('string that should be hashed', '$2y$08$12345678901234567890a' . $chars[$i]) . "\n";
}

在这里:

$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890aeDDHXF42QK8mY.t4/x9I.DNpdmARsDG.
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890auYqXTg7.1WNKn8Yxc4wW2p2ppsJb9rZa
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890aOQs9arCVhxFtQ.Z7yJUOtp8UCDsR1rHa
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO
$2y$08$12345678901234567890a.FpXM0OnHV1fSxeBCiU8eEDae5LtBtAO

请注意,这只产生 4 个唯一哈希值?这是因为我们只使用最后一个字节的前 2 位 (2^2)。其余的实际上是结果哈希的一部分(因此被丢弃)。

有意义?

顺便说一句:出于这个和其他原因,我建议不要直接使用,而是使用库。例如PHP 5.5中的密码,或者它的兼容性库(我维护的)password_compatcrypt()


答案 2

推荐