散列是从某个位串(通常是可变长度)到另一个位串(通常更小,并且具有固定长度)的函数。
散列在数据库中用于数据检索,并在称为散列表的内存数据结构中使用。它允许我们将任意数据(例如字符串或具有许多字段的复杂对象)简化为二进制数,然后可以将其直接用作稀疏数组的索引以获取相关数据(以及处理哈希的一些细节)碰撞)。
以上述方式使用的散列函数是密码散列函数的“表亲”。它们是针对不同的要求而设计的。它们必须快速计算,并实现良好的分布。
在安全计算中,加密哈希用于将数据消化成一些有代表性的小比特串。密码功能有不同的要求。它们被设计成难以逆转(成为“活板门”或“单向”功能)。不仅如此,一个重要的要求是,对于给定的明文和散列值,必须很难找到另一个产生相同散列的明文。
散列不仅可用于密码,还可用作验证数据完整性的校验和以及数字签名实施的一部分。要对大型文档进行数字签名,我们只需对文档进行哈希处理以生成“摘要”(哈希函数输出的名称,当对很长的内容进行哈希处理时)。然后只是这个摘要通过公钥密码系统产生签名。你可以看到那里的弱点:如果攻击者成功地生成了具有相同摘要的文档怎么办?然后看起来在真实文件上产生的原始签名实际上是伪造文件的签名:实际上已经进行了签名移植伪造。
密码散列允许系统不存储密码的纯文本版本,但使它们能够验证试图获取条目的用户是否知道该密码。散列不仅允许系统不存储纯文本密码(必须非常小心地保护),而且它允许即使散列公开,密码仍然是安全的(类似于公钥加密系统能够揭示公钥)。尽管在实践中,哈希仍然受到保护,不被公共访问:例如/etc/shadow
类 Unix 系统上的文件,补充世界可读的/etc/passwd
文件。
散列函数绝不是随机的。然而,随机化被用来阻止攻击者构建大量密码和散列字典,使他们能够查找散列码并检索相应的密码。
为了更安全地散列密码,我们可以简单地添加一些随机位,称为“盐”。当然,将不同的盐添加到相同的密码会导致不同的哈希(希望很少或没有冲突)。
例如,如果随机盐是 32 位宽,这意味着理论上一个密码可以以超过 40 亿种不同的方式散列,这使得拥有大量密码的所有可能散列的预计算字典非常不切实际。
当然,当用户被认证时,她对这个盐一无所知。没关系,因为盐与哈希一起存储在用户配置文件中(通常与哈希组合成一个紧凑的位串)。当验证用户的密码输入时,盐会添加到她输入的任何密码中,以便使用正确的盐执行散列。如果密码正确,则哈希将匹配,因为使用的盐也是正确的,已从用户的配置文件中提取。
这就是随机性如何被纳入密码散列,同时仍然允许它工作。
使哈希难以破解的原因是它们是由“活板门”或“单向”函数构建的。在数学中,有很多这样的例子。例如,简单的加法是一个活板门。如果我们将一些整数相加以产生和,则不可能恢复原始数字,只知道和。
密码哈希不是加密密码。如果攻击者拥有密码的哈希和盐,并且碰巧猜到了密码,那么她可以很容易地确认这一点,就像登录验证器软件所做的一样:她通过哈希函数运行密码和盐,然后看到正确的哈希出现了。