哈希是如何工作的?

信息安全 哈希
2021-09-05 05:37:30

我一直对信息安全感兴趣。我最近被介绍到散列的想法。我目前对散列的理解是它需要用户输入的密码。然后它使用一堆变量随机生成一个“哈希”并对所有内容进行加扰。然后,当您输入此密码进行登录时,它会将该密码与哈希匹配。只是有几件事我不明白。

  1. 为什么破解这些哈希这么难?我假设一旦你找到了他们用来加密它的方法(一旦你发现你必须转移多少,你就可以为整本书做一个非常简单的方法,比如凯撒的密码)。即使它使用诸如时间之类的东西并使其混乱,也有一些非常大的方法可以限制选项(让我们使用他们正在使用的年份 mod x 的凯撒密码,您已经知道实际上有两个可能的年份,那么您只需要找出第二块拼图)。

  2. 如果它们是随机生成的(即使两个密码相同,它们的输出也不同),它们如何判断它是否正确?

  3. 他们是如何破解的。哈希猫如何知道它何时成功解密了密码?

相关视频(但不能完全回答我的问题):https ://www.youtube.com/watch?v=b4b8ktEV4Bg

4个回答

快,系数 1081。

或者,如果您愿意,可以回答这个问题:23 乘以 47 是多少?

哪一个更容易?执行乘法(只是机械地遵循规则)比恢复仅给定乘积的操作数更容易。乘法。(顺便说一下,这是一些加密算法的基础,例如RSA。)

密码哈希函数具有不同的数学基础,但它们具有相同的属性:它们很容易向前计算(计算给定 x 的 H(x)),但实际上不可能向后计算(给定 y,计算 x 使得 H( x) = y)。事实上,一个好的密码散列函数的标志之一是找到 x 没有比尝试所有这些并计算 H(x) 直到找到匹配项更好的方法。

散列函数的另一个重要特性是两个不同的输入具有不同的散列。因此,如果 H(x 1 ) = H(x 2 ),我们可以得出 x 1 = x 2的结论。从数学上讲,这是不可能的——如果输入长于散列的长度,则必须存在冲突。但是对于一个好的密码散列函数,没有已知的方法可以找到与世界上所有计算资源的冲突。

如果您想了解有关加密哈希函数的更多信息,请阅读Thomas Pornin 的这个答案继续,我等着。

请注意,散列函数不是加密函数。加密意味着您可以解密(如果您知道密钥)。有了哈希,就没有什么神奇的数字可以让你回去了。

主要推荐的加密哈希函数是SHA-1SHA-2系列(有多种输出大小,主要是 SHA-256 和 SHA-512)。MD5是较旧的,现在已被弃用,因为它有已知的冲突。最终,没有数学证明它们确实是好的密码散列函数,只是一种普遍的信念,因为许多专业密码学家花了多年的时间试图破解它们,但失败了。

好的,这是故事的一部分。现在密码散列不是直接的加密散列函数。密码哈希函数 (PHF) 有两个输入:密码和盐。当用户选择他的密码时, salt是随机生成的,它与散列密码 PHF(password, salt) 一起存储。(重要的是两个不同的账户总是有不同的salt,随机生成一个足够大的salt是一个以压倒性概率拥有这个属性的好方法。)当用户再次登录时,验证系统从密码数据库中读取salt ,计算 PHF(密码,盐),并验证结果是否存储在数据库中。

加盐的要点是,如果有人想破解密码,他们必须在开始之前知道哈希,并且他们必须分别攻击每个帐户。盐使得无法提前执行大量破解工作,例如通过生成彩虹表

这回答了 (2) 和 (3) — 合法验证者和攻击者以相同的方式找出密码(由用户输入或由攻击者猜测)是否正确。故事的最后一点:一个好的密码哈希函数有一个额外的属性,它必须很慢。合法服务器每次登录尝试只需要计算一次,而攻击者每次猜测都必须计算一次,因此速度慢对攻击者的伤害更大(这是必要的,因为攻击者通常拥有更多的专用硬件)。

如果您需要散列密码,请不要发明自己的方法使用标准方法之一scryptbcryptPBKDF2。 

加密哈希函数是数学对象,可以描述为“一些位的大混合和加扰”。它们将一系列位(可能是一个很长的位)作为输入,并提供固定大小的输出。粗略地说,它们是如此纠结,以至于尽管它们没有什么秘密(这只是确定性代码),但除了称为“运气”的基本方法之外,没有人能弄清楚如何“反转”它们(为给定的输出找到匹配的输入) ":尝试随机输入,直到找到匹配项。

从科学上讲,哈希函数是如何存在的,这是一个很好的问题

散列不是加密散列没有秘密,没有密钥。

哈希函数有很多用途;其中之一是“密码存储”。散列函数看起来是密码存储的好东西。我们不想直接存储密码(否则攻击者偶尔偷看我们的数据库会给他提供太多信息;请参阅此博客文章进行讨论);我们想要存储密码验证令牌:允许验证密码(用户提供)但不泄露密码本身的东西。所以想法是:让我们存储密码的哈希值。当要验证密码时,我们只需计算其哈希值并查看它是否与存储的值匹配。但是仅从哈希值猜测密码是很困难的,因为哈希函数对“反转”具有弹性(见上文)。

由于密码是一种特殊的数据(人类可以记住的数据),为了适当的安全性,我们需要一个“强化”的哈希函数:

  • 我们想要一个非常慢的哈希函数。
  • 我们不想要一个散列函数,而是许多不同的散列函数,这样每个密码都会用自己的散列函数进行散列;这是为了阻止并行攻击。这种将单个散列函数转化为多种变体的过程称为加盐

有关散列密码主题的彻底处理,请参阅此答案

散列是从某个位串(通常是可变长度)到另一个位串(通常更小,并且具有固定长度)的函数。

散列在数据库中用于数据检索,并在称为散列表的内存数据结构中使用。它允许我们将任意数据(例如字符串或具有许多字段的复杂对象)简化为二进制数,然后可以将其直接用作稀疏数组的索引以获取相关数据(以及处理哈希的一些细节)碰撞)。

以上述方式使用的散列函数是密码散列函数的“表亲”。它们是针对不同的要求而设计的。它们必须快速计算,并实现良好的分布。

在安全计算中,加密哈希用于将数据消化成一些有代表性的小比特串。密码功能有不同的要求。它们被设计成难以逆转(成为“活板门”或“单向”功能)。不仅如此,一个重要的要求是,对于给定的明文和散列值,必须很难找到另一个产生相同散列的明文。

散列不仅可用于密码,还可用作验证数据完整性的校验和以及数字签名实施的一部分。要对大型文档进行数字签名,我们只需对文档进行哈希处理以生成“摘要”(哈希函数输出的名称,当对很长的内容进行哈希处理时)。然后只是这个摘要通过公钥密码系统产生签名。你可以看到那里的弱点:如果攻击者成功地生成了具有相同摘要的文档怎么办?然后看起来在真实文件上产生的原始签名实际上是伪造文件的签名:实际上已经进行了签名移植伪造。

密码散列允许系统不存储密码的纯文本版本,但使它们能够验证试图获取条目的用户是否知道该密码。散列不仅允许系统不存储纯文本密码(必须非常小心地保护),而且它允许即使散列公开,密码仍然是安全的(类似于公钥加密系统能够揭示公钥)。尽管在实践中,哈希仍然受到保护,不被公共访问:例如/etc/shadow类 Unix 系统上的文件,补充世界可读的/etc/passwd文件。

散列函数绝不是随机的。然而,随机化被用来阻止攻击者构建大量密码和散列字典,使他们能够查找散列码并检索相应的密码。

为了更安全地散列密码,我们可以简单地添加一些随机位,称为“盐”。当然,将不同的盐添加到相同的密码会导致不同的哈希(希望很少或没有冲突)。

例如,如果随机盐是 32 位宽,这意味着理论上一个密码可以以超过 40 亿种不同的方式散列,这使得拥有大量密码的所有可能散列的预计算字典非常不切实际。

当然,当用户被认证时,她对这个盐一无所知。没关系,因为盐与哈希一起存储在用户配置文件中(通常与哈希组合成一个紧凑的位串)。当验证用户的密码输入时,盐会添加到她输入的任何密码中,以便使用正确的盐执行散列。如果密码正确,则哈希将匹配,因为使用的盐也是正确的,已从用户的配置文件中提取。

这就是随机性如何被纳入密码散列,同时仍然允许它工作。

使哈希难以破解的原因是它们是由“活板门”或“单向”函数构建的。在数学中,有很多这样的例子。例如,简单的加法是一个活板门。如果我们将一些整数相加以产生和,则不可能恢复原始数字,只知道和。

密码哈希不是加密密码。如果攻击者拥有密码的哈希和盐,并且碰巧猜到了密码,那么她可以很容易地确认这一点,就像登录验证器软件所做的一样:她通过哈希函数运行密码和盐,然后看到正确的哈希出现了。

散列的关键之一是它会丢弃信息。您无法反转哈希,因为必要的知识已经消失。以下是一些可行(但毫无价值)的散列函数示例。如果您给我一个密码,我可以执行以下操作之一:

  • 计算元音的数量
  • 获取每个字母的 ASCII 码并将它们全部异或
  • 取密码二进制表示的 CRC32 校验和(这个实际上是一个真正的哈希,只是不是加密的)

在每一种情况下,我都无法逆转这个过程。相反,当您稍后再次给我密码时,我必须重新运行该过程,以查看我运行的计算是否匹配。

例如:如果您最初给我密码“猴子”,我可能会存储数字 3(3 个元音)。然后,稍后当我尝试验证密码“dragon”时,我再次运行相同的检查并得出 2,它与 3 不匹配。所以我知道你给了我错误的密码。但是如果你给我密码“melissa”,我会错误地认为你输入了正确的密码。这是一个哈希冲突

您应用来得出代表给定密码的数字的规则集是您的散列函数这些被认为是“单向”功能,因为您不应该能够反转它们。高质量的哈希函数旨在限制潜在冲突的数量,因此您不必担心这个问题。更进一步,密码散列函数被设计成难以提出可能与给定输出匹配的字符串(并且可能故意造成冲突)。它们还旨在限制您可以仅从哈希输出中收集有关给定输入的信息量。

因此,判断哪个密码与给定加密哈希匹配的唯一方法是尝试所有可能性,直到你偶然发现一个可行的方法。进一步的对策(盐、BPKDF2 等)通过让猜测密码的人每次尝试跳过更多的圈,使这个猜测过程变得更加困难。

请注意,我完全掩盖了加密散列函数如何使想出有效密码变得困难(即使它不是原始密码)。这称为“原像攻击”。在上面的简单示例中,提出“melissa”作为包含 3 个元音的候选密码就是这种攻击的一个示例。

加密散列函数通常通过在给定进程的几“轮”中运行输入来实现这一点,其中每一轮的输出成为下一轮输入的一部分。要计算出第一轮的输入,你必须计算出第二轮的输入,这反过来又需要你计算出第三轮的输入等等,这意味着每个分量的每次猜测必须通过一系列冗长而复杂的计算来检查。Thomas Pornin对这种抵抗力如何发挥作用进行了详尽的解释。如果您想真正理解它,阅读它非常有用。