密码哈希和加盐——这是一种好的方法吗?

7
我在做一些关于密码哈希和盐值处理的研究或谷歌搜索,偶然发现了这个有趣的链接:http://phix.me/salt/。基本上,这个链接提出了创建两个用户函数,一个用于哈希,一个用于检查哈希。
盐是伪随机的,但实际上基于密码(我认为这很糟糕?)。
哈希函数还会在哈希字符串中伪随机地“撒”盐。
哈希检查函数会反转盐撒法,然后进行实际的哈希检查。
现在,我知道每个密码哈希的唯一盐值很好,但是将哈希密码和创建盐值的逻辑存储在数据库函数中可能不好。
我喜欢盐不明显的想法,它也不需要基于某些希望一致的值,比如用户名、用户ID、出生日期等,但是我对实现有所怀疑。
那么,人们对“最佳方法解决方案”的意见和想法是什么呢?

更新链接以指向archive.org的副本。 - Jeffrey Hantin
8个回答

15

使用用户名作为盐是否可行?看起来是达到指数级增加彩虹表大小的目标。 - Peter J
1
@Iconic:有趣的问题。我认为这可能不是最好的想法。一个盐应该对于每个密码都是唯一的,包括密码历史中的任何密码。使用用户名作为盐违反了这个原则。 - Randolpho
4
我同意;从用户名(或其他用户信息)中派生盐值并不可怕,但可能会使某些攻击更容易。如果我想尝试破解在不同网站上拥有相同用户名的用户的密码,而每个网站都将用户名用作盐值,那么创建一个字典可能是值得的。当使用加密随机数发生器选择随机盐值如此有效且简单时,为什么要去改变其他任何东西呢? - erickson
如果各个网站在派生盐值时不以相同方式使用用户名,那么字典将如何工作? - Seun Osewa
有很多网站使用完全相同的密码哈希实现。例如,所有使用内置Tomcat密码身份验证的网站。但你是对的,如果一个网站在派生中做了一个小改变,那么字典将毫无用处。 - erickson

11

盐的目的是为了使使用彩虹表变得非常昂贵,因此“尝试1”基本上正确地解决了这个问题。将盐基于密码消除了打败彩虹表的可变性,尝试隐藏它在哈希密码字段中就毫无意义。


你对我的初始答案是正确的,Jeffrey,谢谢。在我浏览时,我误读了他的选择语句为“concat(md5(concat('password')),@s);”,所以我认为他所做的就是将盐连接到未加盐哈希的末尾。然而,他确实正确地加盐了,文章中的其余技术只是毫无意义的安全性通过混淆的胡言乱语。 - Chad Birch
哦,亲爱的,我也犯了同样的错误。 - BrynJ
2
是的,那是对情况的准确分析。所提出的方案增加了复杂性,但并没有增加安全性。 - Accipitridae

7

我之前问过一个类似的问题。共识是:

重要的不是你如何加盐,而是你:

  1. 在哈希之前加盐
  2. 为每个密码使用独特的随机盐
  3. 使用足够大的盐来阻止彩虹表攻击。

你甚至可以将你的盐存储在与哈希密码相邻的位置,并且感觉十分自信。

个人而言,我发现GUID(以字符串格式)非常适合作为盐。只需要一行代码即可生成,在字符串格式中足够大,使得彩虹表需要数千年才能计算出。


好的技巧,Randolpho。我会加上使用密钥延伸技术,例如反复哈希数千次。一个好的盐可以防止彩虹表攻击,但您需要密钥延伸来减慢对“弱密码空间”(7或8个字符的密码)的暴力搜索。 - erickson
@erickson:这可能可行,但对于服务器来说看起来计算成本很高。 - Randolpho
它会为每个合法用户的身份验证添加毫秒级别的时间,因此在大多数实际应用中,这是可以忽略不计的。只有在离线破解器中进行数百万次尝试时,才会变得“昂贵”。如果没有它,一个人可以在几天内搜索7个字符空间(如果盐不是秘密的话,这是安全的假设)。这假定了一台带有良好图形卡的单机;如果你愿意花一点钱,你可以搜索8或9个字符空间。如果你愿意花很多钱(想想大政府)...那么,关键拉伸是必不可少的。 - erickson
嗯...好的,如果您确实认为性能可以忽略不计,那我一定会对关键绷直进行一些研究。 - Randolpho
关于将唯一的盐与用户的哈希密码一起存储,是否有任何共识? - OldBuildingAndLoan
使用GUID似乎是一种有趣且简单的方法。 - OldBuildingAndLoan

4

(由于我最初误读了文章并认为他只是将盐混合在一个无盐哈希中,因此编辑了回答)

他的技术看起来还不错,它们会起作用,但它们并没有比普通的加盐方法更好。这只是试图通过混淆保障安全性,它不比编写自己的随机“哈希混淆”方法然后希望攻击者无法找出更好。

实际上,在许多情况下,攻击者可能很容易找出这些函数。如果该站点是一个公开注册的站点,攻击者可以反复注册带有已知密码的帐户,然后使用这些密码的已知 MD5 哈希值来逆推密码混淆算法。我甚至可以轻松地完成这个过程,即使是看着他“尝试 4”的结果。

如果您想真正安全地存储密码,请避免使用MD5甚至SHA1,并转向盐值较高、速度较慢的哈希函数。这是一篇关于该主题的好文章:Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes


哦,哇,我甚至没有注意到这个!这肯定对他的方法的有效性产生了怀疑。 - BrynJ
关键在于密码和盐都输入到哈希函数中,并且盐也被存储在其旁边。MD5的弱点可能不会对密码表造成致命影响,但更换哈希函数很容易。 - Jeffrey Hantin
确实。如果我能总结这个答案(并使它成为“答案”),我会说:尝试4混淆了一个已经具有密码学安全性的解决方案。用SHA256或bcrypt替换MD5,该方案就是安全的,除非“密码学方面出现重大进展”。 - Peter J
1
很多时候,这种混淆尝试最终会导致比普通方案更糟糕的方案。因此,你对这里出现错误的期望是可以理解的。 - Accipitridae
@Chad Birch:关于使用已知密码创建账户的观点非常好。但是如果黑客已经入侵了数据库并能够查看每个创建的账户的哈希值,那么这种方法只对黑客有效吧? - OldBuildingAndLoan
显示剩余3条评论

4

3
这对我来说似乎只是为(正如Chad Birch所指出的)加盐哈希方法添加了混淆,而没有增加任何安全性。

1
有趣的是,这不仅是混淆,而不是安全性问题,实际上是混淆,减弱了安全性,因为"尝试4"的安全性与其使用的CRC32函数一样好(CRC32只传递密码,而不是密码+盐)- 这就是失败之处。
根据查德的帖子,要破解"尝试4",只需CRC32加载大量密码,然后反转他编写的函数,得到一个带有盐的md5哈希和盐(您将测试其有效性)。只需通过计算md5(password+salt)(其中password是您正在尝试的密码,salt是通过反转算法计算出来的盐),并且如果md5等于哈希的前32个字符,则已破解密码。
在某些方面,"尝试4"比"尝试1"更糟糕,因为它只像例程中调用的最差函数一样好,即CRC32(密码)。

1

我无法查看原问题中的链接(该网站返回404未找到错误),但问题描述中所述的方法实际上并没有使用盐散列。

本质上,这种方法只是使用了非标准哈希:给定一个特定的密码,将存储在数据库中的是唯一的值。这就足以使彩虹表攻击生效:我可以预先计算出一个可能的密码字典的哈希值并寻找任何匹配项。现在,我必须专门为这种非标准哈希函数预先计算彩虹表。

在正确实现盐散列的情况下,创建密码时将随机盐与密码组合并进行哈希处理。然后使用随机盐和哈希进行存储。即使我知道密码,我也无法预测哈希会是什么,因为每个可能的盐值都有不同的哈希值。现在,攻击者需要为每个可能的盐值预先计算彩虹表:这需要投入更大的精力。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接