在存储密码之前对密码进行两次哈希比只进行一次哈希更安全还是更不安全?

我说的是这样做:

$hashed_password = hash(hash($plaintext_password));

而不是这样:

$hashed_password = hash($plaintext_password);

如果它不太安全,你能提供一个好的解释(或一个链接)吗?

另外,使用哈希函数有区别吗?如果混合使用md5和sha1(例如),而不是重复相同的哈希函数,会有任何区别吗?

注1:当我说“双重哈希”时,我指的是对密码进行两次哈希,以使其更加模糊。我说的不是解决碰撞的技术。

注2:我知道我需要添加一个随机的盐,以真正使它安全。问题是用同一个算法进行两次哈希运算对哈希是有利还是有害。


当前回答

是的——它减少了可能匹配字符串的数量。

正如你已经提到的,咸散列更好。

这里有一篇文章:http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/,试图证明为什么它是等价的,但我不确定逻辑。一方面,他们假设没有软件可以分析md5(md5(文本)),但很明显,生成彩虹表是相当简单的。

我仍然坚持我的答案,md5(md5(文本))类型哈希的数量比md5(文本)哈希的数量少,增加了碰撞的机会(即使仍然是一个不太可能的概率),并减少了搜索空间。

其他回答

减少搜索空间的担忧在数学上是正确的,尽管搜索空间仍然足够大,对于所有实际目的(假设您使用盐),在2^128。然而,由于我们谈论的是密码,根据我的粗略计算,可能的16个字符的字符串(字母数字,大写,一些符号)的数量大约是2^98。所以搜索空间减少的感觉并不是真的相关。

除此之外,从密码学的角度来说,实际上没有什么不同。

Although there is a crypto primitive called a "hash chain" -- a technique that allows you to do some cool tricks, like disclosing a signature key after it's been used, without sacrificing the integrity of the system -- given minimal time synchronization, this allows you to cleanly sidestep the problem of initial key distribution. Basically, you precompute a large set of hashes of hashes - h(h(h(h....(h(k))...))) , use the nth value to sign, after a set interval, you send out the key, and sign it using key (n-1). The recepients can now verify that you sent all the previous messages, and no one can fake your signature since the time period for which it is valid has passed.

像Bill建议的那样重新哈希几十万次只是浪费你的cpu。如果你担心别人会破坏128位,可以使用更长的密钥。

根据我所读到的,实际上可能建议将密码重新哈希数百或数千次。

其思想是,如果您可以花费更多的时间来编码密码,那么攻击者就需要做更多的工作来进行多次猜测以破解密码。这似乎是重哈希的优势——并不是说它在密码学上更安全,而是它只是需要更长的时间来生成字典攻击。

当然,计算机一直在变得更快,所以这种优势会随着时间的推移而减弱(或者需要您增加迭代)。

一次哈希密码是不安全的

不,多重哈希并不是更不安全;它们是安全使用密码的重要组成部分。

迭代散列会增加攻击者尝试候选密码列表中的每个密码所需的时间。您可以轻松地将攻击密码所需的时间从数小时增加到数年。

简单的迭代是不够的

仅仅将哈希输出链接到输入不足以保证安全性。迭代应该在保留密码熵的算法上下文中进行。幸运的是,有几个已经发表的算法已经经过了足够的审查,使他们对自己的设计充满信心。

像PBKDF2这样的良好的密钥派生算法将密码注入到每一轮哈希中,从而减轻了对哈希输出中冲突的担忧。PBKDF2可以按原样用于密码身份验证。Bcrypt使用加密步骤进行密钥推导;这样,如果发现了反向密钥推导的快速方法,攻击者仍然必须完成已知的明文攻击。

如何破解密码

存储的密码需要防止离线攻击。如果密码没有加盐,它们可以被预先计算的字典攻击破坏(例如,使用彩虹表)。否则,攻击者必须花时间为每个密码计算哈希,并查看它是否与存储的哈希匹配。

并非所有密码的可能性都是一样的。攻击者可能会穷尽搜索所有短密码,但他们知道,每增加一个字符,暴力破解成功的几率就会急剧下降。相反,他们使用最可能的密码的有序列表。他们从“password123”开始,逐步使用不太常用的密码。

假设攻击者名单很长,有100亿候选人;再假设一个桌面系统每秒可以计算100万次哈希。如果只使用一次迭代,攻击者可以在不到3小时的时间内测试她的整个列表。但如果只使用2000次迭代,时间就会延长到近8个月。要打败一个更复杂的攻击者——例如,能够下载一个可以利用他们GPU能力的程序的攻击者——你需要更多的迭代。

多少才够?

使用的迭代次数是安全性和用户体验之间的权衡。攻击者可以使用的专用硬件很便宜,但它仍然可以每秒执行数亿次迭代。攻击者系统的性能决定了在给定的迭代次数下需要多长时间才能破解密码。但是您的应用程序不太可能使用这种专用硬件。在不惹恼用户的情况下,您可以执行多少迭代取决于您的系统。

您可能可以在身份验证期间让用户多等待3 / 4秒左右。分析您的目标平台,并使用尽可能多的迭代。我测试过的平台(移动设备上的一个用户,或者服务器平台上的许多用户)可以轻松地支持6万到12万次迭代的PBKDF2,或者成本因子为12或13的bcrypt。

更多的背景

Read PKCS #5 for authoritative information on the role of salt and iterations in hashing. Even though PBKDF2 was meant for generating encryption keys from passwords, it works well as a one-way-hash for password authentication. Each iteration of bcrypt is more expensive than a SHA-2 hash, so you can use fewer iterations, but the idea is the same. Bcrypt also goes a step beyond most PBKDF2-based solutions by using the derived key to encrypt a well-known plain text. The resulting cipher text is stored as the "hash," along with some meta-data. However, nothing stops you from doing the same thing with PBKDF2.

以下是我写的关于这个话题的其他答案:

哈希密码 哈希密码 盐 隐藏的盐 PBKDF2和bcrypt Bcrypt

大多数答案都是由没有密码学或安全背景的人回答的。但他们错了。使用一个盐,如果可能的话,每个记录都是唯一的。MD5/SHA/等等太快了,与你想要的相反。PBKDF2和bcrypt较慢(这很好),但可以被asic /FPGA/ gpu击败(现在非常实惠)。因此需要一个内存硬的算法:输入scrypt。

这里是关于盐和速度的一个外行解释(但不是关于内存硬算法)。

一般来说,它不会为双重哈希或双重加密提供额外的安全性。如果你能分解一次散列,你就能再分解一次。不过,这样做通常不会损害安全性。

在使用MD5的例子中,您可能知道有一些碰撞问题。“双重哈希”并不能真正帮助防止这种情况,因为相同的碰撞仍然会导致相同的第一个哈希,然后您可以再次MD5以获得第二个哈希。

这确实可以防止字典攻击,比如那些“反向md5数据库”,但盐也是如此。

在切线上,双重加密某些东西并不能提供任何额外的安全性,因为它所做的只是导致一个不同的密钥,这是实际使用的两个密钥的组合。因此寻找“钥匙”的工作不会加倍,因为实际上不需要找到两把钥匙。这对于哈希并不适用,因为哈希的结果通常与原始输入的长度不同。