与此处最赞的答案强调的相反,密码哈希函数的非注入性(即存在多个字符串散列到同一值)由于大输入大小(可能为无限)和固定输出大小之间的差异所导致并不是重��� - 实际上,我们更喜欢尽可能少发生碰撞的哈希函数。
考虑以下函数(使用PHP表示法,如问题所示):
function simple_hash($input) {
return bin2hex(substr(str_pad($input, 16), 0, 16));
}
如果字符串太短,它会添加一些空格,然后取字符串的前16个字节,并将其编码为十六进制。输出大小与MD5散列相同(32个十六进制字符或者如果省略bin2hex部分,则为16个字节)。
print simple_hash("stackoverflow.com")
这将输出:
737461636b6f766572666c6f772e636f6d
这个函数和MD5一样也有不可逆性,正如Cody的答案中所强调的:我们可以传入任意大小(只要它们适合我们的计算机),它只会输出32个十六进制数字。当然它不能是可逆的。
但在这种情况下,很容易找到一个映射到相同哈希的字符串(只需在哈希上应用hex2bin
即可)。如果原始字符串的长度为16(如我们的示例),您甚至将获得此原始字符串。对于MD5来说,这种情况不可能发生,即使您知道输入的长度非常短(除非尝试所有可能的输入,直到找到一个匹配的输入,例如暴力攻击)。
密码哈希函数的重要假设包括:
- 很难找到任何产生给定哈希的字符串(预像阻力)
- 很难找到任何产生与给定字符串相同哈希的不同字符串(第二预像阻力)
- 很难找到任何具有相同哈希的字符串对(碰撞防止)
显然,我的simple_hash
函数都没有满足这些条件。(实际上,如果我们将输入空间限制为“16字节字符串”,则我的函数变得可逆,因此甚至可以被证明具有第二预像防护和碰撞防护)。
现在已经存在针对MD5的碰撞攻击(例如,即使具有给定相同前缀,也可以生成具有相同哈希的一对字符串,但需要一些工作,但不是无法完成),因此您不应该将MD5用于任何关键任务。目前还没有预像攻击,但攻击会变得更好。
回答实际问题:
这些函数具有什么特点,使得结果字符串不可能被追溯?
MD5(以及其他基于Merkle-Damgard构造的哈希函数)有效地执行的操作是使用消息作为密钥和某个固定值作为“明文”应用加密算法,使用生成的密文作为哈希值。(在此之前,输入进行了填充并分成块,每个块都用于加密上一个块的输出,并与其输入做异或运算以防止反向计算。)
现代加密算法(包括用于哈希函数的算法)通常采用位混淆操作的方式来难以恢复密钥,即使给定明文和密文(或者即使对手选择其中之一)。他们这样做通常是通过以每个密钥位(多次)和每个输入位确定每个输出位的方式进行的。这样,只有在您知道完整密钥和输入或输出时才能轻松追溯发生了什么。
对于类似MD5的哈希函数及其预像攻击(使用单个块散列字符串,以简化事情),您仅具有加密函数的输入和输出,但没有密钥(这就是您要寻找的内容)。