MD5 设计为cryptographically irreversible。在这种情况下,最重要的属性是在计算上找到哈希的反向是不可行的,但是很容易找到任何数据的哈希。例如,让我们考虑只对数字进行操作(毕竟二进制文件可以解释为一个很长的数字)。
假设我们有数字“7”,我们想要获取它的哈希值。也许我们作为散列函数尝试的第一件事是“乘以 2”。正如我们将看到的,这不是一个很好的散列函数,但我们会尝试一下,以说明一点。在这种情况下,数字的哈希值将是“14”。这很容易计算。但是现在,如果我们看看扭转它有多难,我们会发现它也一样容易!给定任何哈希值,我们只需将其除以 2 即可得到原始数字!这不是一个好的哈希,因为哈希的全部意义在于计算逆比计算哈希要困难得多(至少在某些情况下,这是最重要的属性)。
现在,让我们尝试另一个哈希。对于这一点,我将不得不介绍时钟算法的概念。在时钟上,没有无限数量的数字。事实上,它只是从 0 到 11(请记住,时钟上的 0 和 12 是相同的)。因此,如果您在 11 上“加一”,您将得到零。您可以将乘法、加法和求幂的概念扩展到时钟。例如,8+7=15,但时钟上的 15 真的只是 3!所以在时钟上,你会说 8+7=3! 6*6=36,但在时钟上,36=0!所以 6*6=0!现在,对于权力的概念,你可以做同样的事情。 2^4=16,但 16 就是 4。所以 2^4=4!现在,这是它与散列的关系。我们试试散列函数 f(x)=5^x 怎么样,但是使用时钟算法。正如您将看到的,这会导致一些有趣的结果。让我们尝试像以前一样取 7 的哈希值。
我们看到 5^7=78125,但在时钟上,这只是 5(如果你算一下,你会发现我们已经在时钟上绕了 6510 次)。所以我们得到 f(7)=5。现在,问题是,如果我告诉你我的号码的哈希是 5,你能算出我的号码是 7 吗?嗯,在一般情况下,实际上很难非常计算这个函数的倒数。比我聪明得多的人已经证明,在某些情况下,反转这个函数比向前计算要困难方式。 (编辑:Nemo 指出这实际上还没有被“证明”;事实上,你得到的唯一保证是很多聪明人已经尝试了很长时间来找到一种简单的方法,并且没有一个成功。) 反转这个操作的问题被称为“Discrete Logarithm Problem”。查找它以获得更深入的报道。这至少是 good 散列函数的开始。
对于现实世界的哈希函数,想法基本相同:您会发现一些难以反转的函数。比我聪明得多的人设计了 MD5 和其他哈希值,使它们很难被证明是逆向的。
现在,您可能更早地想到了:“计算倒数很容易!我只需对每个数字进行哈希处理,直到找到匹配的那个!”现在,对于数字都小于十二的情况,这将是可行的。但是对于现实世界的哈希函数的模拟,想象所有涉及的数字都是巨大的。这个想法是,计算这些大数的散列函数仍然相对容易,但是搜索所有可能的输入变得更快更难。但是您偶然发现的仍然是一个非常重要的想法:在输入空间中搜索将提供匹配输出的输入。彩虹表是这个想法的一个更复杂的变体,它以智能的方式使用预先计算的输入输出对表,以便快速搜索大量可能的输入。
现在假设您正在使用哈希函数在您的计算机上存储密码。这个想法是这样的:计算机只存储正确密码的哈希值。当用户尝试登录时,您将输入密码的哈希值与正确密码的哈希值进行比较。如果它们匹配,则假定用户具有正确的密码。这是有利的原因是因为如果有人窃取了您的计算机,他们仍然无法访问您的密码,只能访问它的哈希值。因为哈希函数是由聪明人设计的,很难反其道而行之,所以他们不能轻易地从中取回您的密码。
攻击者的最佳选择是暴力攻击,他们会尝试使用一堆密码。就像您可能会尝试上一个问题中小于 12 的数字一样,攻击者可能会尝试所有仅由长度小于 7 个字符的数字和字母组成的密码,或者字典中出现的所有单词。这里重要的是他不能尝试所有可能的密码,因为有太多可能的16字符密码,例如,永远测试。因此,关键是攻击者必须限制他测试的可能密码,否则他将永远不会检查其中的一小部分。
现在,至于盐,想法是这样的:如果两个用户有相同的密码怎么办?它们将具有相同的哈希值。如果您考虑一下,攻击者实际上并不需要单独破解每个用户的密码。他只是简单地检查所有可能的输入密码,并将哈希值与所有哈希值进行比较。如果它与其中一个匹配,那么他已经找到了一个新密码。我们真正想要强迫他做的是为他想要检查的每个用户+密码组合计算一个新的哈希值。这就是盐的想法,就是你让每个用户的哈希函数略有不同,所以他不能为所有用户重用一组预先计算的值。最直接的方法是在获取哈希之前为每个用户的密码添加一些随机字符串,其中每个用户的随机字符串不同。因此,例如,如果我的密码是“shittypassword”,我的哈希可能会显示为 MD5(“6n93nshittypassword”),如果您的密码是“shittypassword”,那么您的哈希可能会显示为 MD5(“fa9elshittypassword”)。这一点“fa9el”被称为“盐”,每个用户都不同。例如,我的盐是“6n93n”。现在,附加在您密码上的这一点也只是存储在您的计算机上。当您尝试使用密码 X 登录时,计算机可以计算 MD5("fa9el"+X) 并查看它是否与存储的哈希匹配。
所以登录的基本机制保持不变,但对于攻击者来说,他们现在面临着更艰巨的挑战:他们面临的不是 MD5 哈希列表,而是 MD5 和和盐的列表。他们基本上有两种选择:
他们可以忽略哈希被加盐的事实,并尝试使用他们的查找表来破解密码。但是,他们实际破解密码的可能性大大降低。例如,即使“shittypassword”在他们要检查的输入列表中,很可能“fa9elshittypassword”不在。为了使破解密码的概率达到他们之前的一小部分,他们需要测试更多可能的密码数量级。
他们可以根据每个用户重新计算哈希值。因此,他们不是计算 MD5(passwordguess),而是为每个用户 X 计算 MD5(Salt_of_user_X + passwordguess)。这不仅迫使他们为他们想要破解的每个用户计算一个新的哈希值,而且最重要的是,它阻止了他们使用预先计算好的表(例如彩虹表),因为他们不知道什么Salt_of_user_X 在手头,因此他们无法预先计算要测试的哈希值。
因此,基本上,如果他们尝试使用预先计算的表格,那么有效地使用盐会大大增加他们为了破解密码而必须测试的可能输入,即使他们没有使用预先计算的表格,它仍然会使它们减慢 N 倍,其中 N 是您存储的密码数量。
希望这能回答你所有的问题。