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

我说的是这样做:

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

而不是这样:

$hashed_password = hash($plaintext_password);

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

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

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

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


当前回答

是的,重哈希减少了搜索空间,但不,这无关紧要——有效的减少是微不足道的。

重散列增加了强制执行所花费的时间,但只执行两次也是次优的。

您真正想要的是使用PBKDF2对密码进行哈希,这是一种经过验证的使用盐和迭代的安全哈希的方法。看看这个SO回应。

编辑:我几乎忘了-不要使用MD5!!!!使用现代加密哈希,如SHA-2家族(SHA-256, SHA-384和SHA-512)。

其他回答

我要大胆地说,在某些情况下,它更安全……不过先别给我投反对票!

从数学/密码学的角度来看,它不太安全,我相信其他人会给你一个比我更清楚的解释。

然而,存在大量的MD5哈希数据库,其中更可能包含“密码”文本而不是它的MD5。所以通过双重哈希,你会降低这些数据库的有效性。

当然,如果你使用盐,那么这个优点(缺点?)就消失了。

是的,重哈希减少了搜索空间,但不,这无关紧要——有效的减少是微不足道的。

重散列增加了强制执行所花费的时间,但只执行两次也是次优的。

您真正想要的是使用PBKDF2对密码进行哈希,这是一种经过验证的使用盐和迭代的安全哈希的方法。看看这个SO回应。

编辑:我几乎忘了-不要使用MD5!!!!使用现代加密哈希,如SHA-2家族(SHA-256, SHA-384和SHA-512)。

一次哈希密码是不安全的

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

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

简单的迭代是不够的

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

像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

就我个人而言,我不会为多个散列而烦恼,但我会确保也散列UserName(或另一个User ID字段)以及密码,这样两个具有相同密码的用户最终不会得到相同的散列。为了更好地度量,我可能还会在输入字符串中加入其他常数字符串。

$hashed_password = md5( "xxx" + "|" + user_name + "|" + plaintext_password);

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

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