如果散列是在 PHP 中使用基于 DES 的 crypt函数完成的,并且攻击者知道散列和盐,是否可以恢复散列密码?
考虑以下示例:
$salt = 'mysalt';
$pass = 'mypass';
$hashed_pass = crypt($pass, $salt);
// $hashed_pass = myDUAMR/WMo7.
我知道开膛手约翰可以打破这一点,但是它不会返回密码,而是会返回一个可用于创建相同哈希值的字符串。在给定的场景下可以恢复真实密码吗?
如果散列是在 PHP 中使用基于 DES 的 crypt函数完成的,并且攻击者知道散列和盐,是否可以恢复散列密码?
考虑以下示例:
$salt = 'mysalt';
$pass = 'mypass';
$hashed_pass = crypt($pass, $salt);
// $hashed_pass = myDUAMR/WMo7.
我知道开膛手约翰可以打破这一点,但是它不会返回密码,而是会返回一个可用于创建相同哈希值的字符串。在给定的场景下可以恢复真实密码吗?
开膛手约翰通过非常快速地尝试可能的密码来工作。它不会总是破坏基于 DES 的密码,或者至少不会轻易破坏。
使用基于 DES 的散列函数,密码最多可以有 8 个字符,每个字符只使用 7 位(忽略高位)。考虑到密码是在某个时候由用户在键盘上键入的,可以假设每个字符有 95 个可能的值(从空格 [32] 到波浪号 [127] 的 ASCII 字符),因此有 6704780954517121 个潜在密码(即 1 + 95 + 95 2 + 95 3 + ... + 95 8)。很多;在 PC 上列举所有可能性至少需要几周时间。但这在技术上仍然是可行的。
DES是一个加密函数,但是基于DES的crypt不是DES;内部函数被更改(即通过盐),执行 25 次,更重要的是,交换了密钥和消息的角色。最终结果是“crypt”这个名字是不恰当的(虽然是传统的):这不再是一个加密函数;它应该被称为“基于DES的哈希”。这意味着可能存在(并且确实存在)两个不同的密码,它们的哈希值相同(即使它们使用相同的盐)。例如,这篇博客文章显示,使用盐“ hi”,“ cqjmide”和“ ifpqgio”都哈希到相同的值“hiH9IOyyrrl4k”(博客文章声称这是第一个公开的此类冲突,这表明问题有多么有趣,因为它并不难:这些是 64 位值,因此发现冲突的成本约为 2 32 次调用crypt(); 第一次公布的碰撞仅出现在 2010 年底,这只是证明在此之前没有人真正尝试过)。
(注意:与博客文章所说的相反,发现冲突的可能性对密码散列系统的安全性没有任何影响。不应再使用基于 DES 的散列,这不是因为容易产生此类冲突,但是因为它的输入和输出空间太小,而且速度太快。)
因此,仅从盐和散列来看,无法确定“密码”,因为信息不存在。通过枚举所有可能的密码,可以构建匹配密码列表,但无法判断用户想到的是哪个密码。然而,这就是这里的重点,在密码验证系统中使用基于 DES 的哈希结果的机器也无法分辨:任何与盐和哈希值匹配的密码都是好的密码,与其他任何密码一样真实。如果 salt 和 hash 都是 " hiH9IOyyrrl4k",那么 " cqjmide" 和 " ifpqgio" 都被接受:即使你认为你的密码是 "cqjmide",我仍然可以使用 "ifpqgio" 作为密码以你的名字登录。
但是请注意,绝大多数可能的哈希值都有一个匹配的密码,因此详尽的搜索将找到该密码,而不是其他密码。
同样,由于只使用前 8 个字符,您可以有一个 40 个字符长的密码,其中最后 32 个字符将被基于 DES 的哈希完全忽略。没有办法从散列值中恢复它们,因为它们只是被丢弃并且对计算的散列没有任何影响。
您不可能计算给定密码的所有冲突(使用您的哈希算法),它们是无限的。如果约翰可以给你一个与哈希摘要匹配的密码,那么理论上你可以恢复真正的密码。但你不能:
找到密码的最佳方法是尝试字典攻击并假设用户使用了该字典的一个单词(或您愿意计算的变体)。您不太可能从两个不同的字典单词中找到两个冲突。因此,这将使您可以很好地确定拥有原始密码。但这当然只是统计数据。