为什么 MD5 哈希从 $1$ 开始,而 SHA-512 从 $6$ 开始?本身不就是弱点吗?

信息安全 哈希 md5
2021-08-20 15:25:49

我已经把这个问题从 stackoverflow 移到了这个地方。我知道这可能是关于“意见”的问题,但我不是在寻找个人意见,而是最终决定保持这种方式的来源。

我被教导说,当一个人甚至不知道门存在时,没有人会试图打开一扇门。最好的防御就是隐藏一扇门。在旧战争电影中很容易看到这一点——没有人会在阳光下藏身。它总是覆盖着一些暗示“那里没有什么有趣的东西”的东西。

我会假设在密码学中它会以同样的方式工作。那么为什么 MD5 生成的散列会从 $1$ 开始,并首先告诉这是什么散列,然后它是什么类型的散列(MD5)?

现在,我看到 sha512 做了完全相同的事情。本身不是弱点吗?有什么特别的理由让我们这样做吗?

主要问题是:我是否应该在存储哈希之前对其进行加扰以向潜在敌人隐藏它?如果不需要,那为什么?

为了避免暗示默默无闻不是安全的答案,我会提出这张照片。这是二战。你刚刚收到一个提示,SS 会来你家,怀疑你在隐藏游击队员,这是真的。他们没有时间逃跑。你有两个选择可以把它们藏起来——在世界上最好的保险箱里,或者在地板下面的隐藏洞里,隐藏得很好,即使你的父母也不会怀疑它在那里。你的提议是什么?你会说服自己最好的保险箱是最好的选择吗?

如果我知道一个岛上隐藏着宝藏,那么我想知道它是哪个岛,否则我不会开始寻找。

我还是不相信。到目前为止,当 Chris Jester-Young 建议可以有更多的算法从不同的数据中生成相同的哈希值时,他给了我一些思考。

3个回答

首先,Kerckhoffs 原则始终是可取的:

一个密码系统应该是安全的,即使系统的所有内容,除了密钥,都是公共知识。

在这种情况下,密码是密钥。因此,保持密码系统的秘密不是目标。

其次,您对那些是 md5 或 sha512 哈希是错误的;存储在您的值/etc/shadow是 md5crypt 或 sha512crypt,这涉及加强过程(多轮 md5 或 sha512 哈希)。

现在,如果您的四个选择是 MD5crypt、sha256crypt、sha512crypt 和 bcrypt(Linux 系统中最流行的选择),这里有四个散列,全部使用$saltsalt$(或等效)作为盐和散列密码生成not my real password

>>> import crypt
>>> crypt.crypt('not my real password','$1$saltsalt')
'$1$saltsalt$4iXfpnrgHRXkrDbPymCE4/'

>>> crypt.crypt('not my real password','$5$saltsalt')
'$5$saltsalt$E0bMpsLR71z8LIvd6p2tD4LZ984JxyD7B9lPLhq4vY7'

>>> crypt.crypt('not my real password','$6$saltsalt')
'$6$saltsalt$KnqiStSM0GULvZdkTBbiPUhoHemQ7Q06YnvuJ0PWWZbjzx3m0RCc/hCfq54Ro3fOwaJdEAliX9igT9DD2oN1u/'

>>> import bcrypt
>>> bcrypt.hashpw('not my real password', "$2a$12$saltsaltsaltsaltsalt..")
'$2a$12$saltsaltsaltsaltsalt..FW/kWpMA84AQoIE.Qg1Tk5.FKGpxBNC'

即使没有注释,也很容易弄清楚它们各自使用哪种方案(md5crypt、sha256crypt、sha512crypt 和 bcrypt 分别长 34、55、98 和 60 个字符(在带注释和盐的 base64 编码中)。所以除非你建议截断散列,或更改散列属性注释以保持一致性不会失去任何安全性。它还为您提供了一种优雅更新用户密码的方法。如果您认为 md5crypt 不再安全,您可以将用户的散列切换为bcrypt 在下次登录时(然后在一段时间后停用所有留在 md5crypt 上的帐户)。或者如果您的算法(如 bcrypt(当时是 2 美元)需要更新,因为设计中的缺陷,您可以轻松识别有缺陷的方案当固定方案达到$ 2a $时。

更糟糕的是,您可以尝试说,我将使用新的常量和圆键修改 sha512。那会让它变得超级难打破——对吧?不,它只是让你很难知道你没有意外引入一个重大漏洞。如果他们可以访问您的 /etc/shadow,他们可能还可以访问用于登录的库,并且随着时间的推移可以对您的哈希方案进行逆向工程,这将比破解强密码简单得多。

同样,暴力破解存储在 sha256 散列中的非常强的密码短语的预期时间是 O(2^256),例如,十亿台计算机每纳秒执行十亿个 sha256crypts (每个涉及约 5000 轮 sha256),将需要 300000000000000000000000(3 x 10^23) 乘以宇宙的年龄来打破它。使用 sha512crypt,如果可观测宇宙中约 10^80 个原子中的每一个每纳秒执行 10 亿次 sha512crypt,则仍需要宇宙年龄的 10^38 倍。(这假设您拥有 256 位和 512 位或更高的熵密码)。

掩盖使用了哪个散列使得系统无法验证合法用户的密码。

当在 Unix 中首次发明通过散列的密码验证时,密码散列函数被硬编码为使用 DES(现在严重过时)。如果密码散列是由任何其他函数派生的,则必须有一个标识符以允许系统识别用于生成散列的算法。

这是因为密码散列是一种单向函数。我听说过希望反向运行这样一个单向函数就像希望反向运行一个香肠工厂并让猪从另一端出来。所以当你去验证某人登录到系统时,你只能向前运行散列函数并将结果与​​存储在 /etc/shadow 中的结果进行比较。

所以这些标识符必须保持清晰,否则会使用错误的散列函数,散列不匹配,没有人可以进入系统。

试图掩盖哈希实现实际上只是将问题从

  • 我可以通过协议看到哈希函数是 X()

  • 查看代码**,我可以看出哈希函数是 X()

**(或其他信息渠道)

如果您的攻击者有任何方法来探测系统,他们几乎肯定能够找出您正在使用的算法。最简单的方法是简单地查看代码(源代码或机器代码),更复杂的方法涉及使用时序测量来区分正在使用的功能。用你的“隐藏的人”类比——我只需要带着一些探测设备走上去,我什至可以在我进入大楼之前就知道那里有地下房间。 

我知道你不想听到它,但现实是“通过默默无闻的安全”不可避免地失败,因为无法从所有可能的方法中充分模糊,无论是今天已知的方法还是未来的一些方法。