使用密码作为盐值的MD5哈希算法?

11
md5($password.md5($password))

这个用于密码哈希的是否足够好?我不是在比较它和像bcrypt这样的东西。

如果不安全,请告诉我原因。


md5($password.md5($id)),这样会更好吗? - wiiman
是的,@wiiman,使用用户ID作为盐更好。我会稍微扩展一下我的答案。 - Don Kirkby
另外,还可以查看Openwall的PHP密码哈希框架(PHPass)。它是可移植的,并且针对用户密码的许多常见攻击进行了加固。编写该框架的人(SolarDesigner)也是编写John The Ripper并担任密码哈希竞赛的评委的人。因此,他对密码攻击有一定的了解。 - jww
6个回答

33
为每个用户的密码使用不同的盐是为了防止攻击者获取一份哈希密码列表并查看它们是否与易识别密码(如“password”或“12345”的哈希)匹配。如果使用密码本身作为盐,攻击者可以计算md5("12345".md5("12345"))并查看是否匹配任何条目。
据我了解,在密码表中可以使用四个级别的哈希处理:
1. - 以明文形式存储密码。如果有人获取了数据库的副本,则可以访问所有帐户。明文存储很危险。 2. 仅哈希处理 - 存储密码的哈希值,并丢弃真实密码。如果有人获取了数据库的副本,则无法查看任何密码,只能看到哈希值。然而,如果用户使用弱密码,则其哈希值将出现在彩虹表中。例如,如果用户的密码是“password”,则在数据库中存储的md5哈希值为“5f4dcc3b5aa765d61d8327deb882cf99”。如果我在像gromweb.com上的彩虹表中查找该哈希值,则会输出“password”。
  • 使用盐值 - 选择一个类似 GUID 的大随机字符串并将其存储在配置文件中。在计算哈希之前,将该字符串附加到每个密码上。现在彩虹表的效率要低得多,因为它可能没有“password59fJepLkm6Gu5dDV”或“picard59fJepLkm6Gu5dDV”的条目。尽管预先计算的彩虹表不再那么有效,但如果攻击者知道您的盐值,仍然存在易受攻击的风险。攻击者可以计算弱密码的哈希值加上您的盐值,并查看任何用户是否使用了该弱密码。如果您有几千个用户,则每个哈希计算可让攻击者进行几千次比较。如何实际使用盐可能取决于您正在使用的加密算法。为简单起见,只需将盐和密码追加在一起。
  • 使用不同的盐值 - 现在您需要取一些唯一的东西,例如用户名、电子邮件地址或甚至用户 ID,并在计算哈希之前将其与密码和来自配置文件的大随机字符串相结合。现在,即使攻击者知道您的盐值,他还必须重新计算每个用户的哈希值才能看到他们是否使用了像“password”这样的弱密码。
  • 有关更多详细信息,请查看 Coding Horror 文章,“您可能存储密码不正确”。


    1
    你的回答中哪一部分是针对特定的提问者的? - Your Common Sense
    我不确定你在第一条评论中提到的攻击者计算是什么意思,@Col。如果你问的是什么使得选项3比选项2更难破解,那就是选项2可以使用预先计算的彩虹表进行攻击,并且攻击者不需要做太多工作。 - Don Kirkby
    我不太确定你所说的“一些弱密码的巧合”是什么意思,@Col。我假设攻击者可以访问系统的所有信息,但无法获取实际用户密码。此外,还有一个按频率排序的十亿个最可能的密码列表和一个预先计算好的百万个最可能的密码的彩虹表。我列出的任何一种可能性都不能防止攻击者提取其中一个密码,但每种可能性都会增加攻击者查找密码所需的处理时间。 - Don Kirkby
    3
    您应该对密码进行加盐处理,使用一个独特的值来存储明文或作为哈希值的一部分。不确定您在这里的问题是什么。 - Adam Robinson
    @Don Kirby,“处理时间”对于处理像登录这样的交易并不是一个非常重要的问题。实际上,这种情况本来就不经常发生。事实上,这篇文章提到了延长认证时间的一些有趣好处。http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html - Kenny Cason
    显示剩余5条评论

    4
    虽然对我来说似乎足够了,但如果有人基于相同算法预先计算出彩虹表,它就会处于危险之中(这是很可能的)。 因此,我宁愿使用电子邮件进行盐处理,这似乎非常安全且可用。偏执者可以在整个站点上添加一些固定的盐。
    人们经常在理论上过分强调密码盐(理论上),而在他们的应用程序中实际上允许简单的密码并以明文形式通过不安全的HTTP传输它们。
    每天都会看到关于盐或哈希的问题。 却没有一个关于密码复杂度的问题。
    你唯一需要关注的是密码复杂度。
    为什么?让我给你展示。
    极好的盐 + 弱密码 = 几秒钟内就能破解
    通常假设攻击者知道盐。因此,通过使用一些最常用密码的字典并向它们添加[任何额外的随机超长]盐,可以在几秒钟内发现弱密码。对于暴力破解短密码也是如此。
    合理的盐 + 强密码 = 不可破解
    相当独特的盐使预计算表无用,好的密码使字典和暴力攻击都变得无用。

    @Col. Shrapnel:关于您最后的评论:说得好。但截获单个密码并不像获取整个表格那样具有问题。当然,除非嗅探器在门口而不是偶然用户端监听。 - Decent Dabbler
    随机盐是正确的密码管理方案中使用最广泛的方法。一个简单的16位随机盐使彩虹表的计算和存储花费增加了65536倍... - Bruno Rohée
    OP提出的是依赖于密码而非随机的盐。实际上这根本不算盐。假设我想要存储所有四位小写字母密码的哈希值:他的方案只需要4^26 * 128个比特,因为每个密码只能导致一个哈希值。使用16位种子,每个用户密码现在可以有2^16个哈希值,相应地会增加2^16倍的磁盘空间。采用32位盐,电脑需要进行非常长时间的计算才能存储彩虹表,即使对这个问题投入大量硬件资源。 - Bruno Rohée
    1
    他的计划中并没有密码和磁盘表示之间的一对一关系。使用随机盐,有n个可能的盐,密码和磁盘表示之间存在一对n的关系,这使得预先计算哈希值变得更加昂贵。 - Bruno Rohée
    @Bruno,你是在说巧合密码吗? - Your Common Sense
    显示剩余2条评论

    2

    它对字典攻击没有太大作用,与单个md5相比,计算一个字典只有两倍难度,并且现在的md5非常便宜。


    2
    不是真的。使用固定盐或您的方法计算字典的难度并没有增加。复杂度相同。 - Mat
    同样适用于随机盐,不是吗? - Your Common Sense
    @Shrapnel 上校:不会,因为那样你就得尝试每个单词与唯一的盐对比,对于每个密码而言,这使得它成为一个更加劳动密集型的过程。有了一个已知的单一盐,您只需要创建一个字典一次即可。 - Decent Dabbler
    但它不是单一已知的盐分。所有这些盐分都是不同的! - Your Common Sense
    1
    @Col. Shrapnel:我觉得这里有些误解。你可能误解了我的意思。我认为我们在谈论同一件事情。所有密码使用单一已知盐值=更糟糕。每个密码使用唯一的盐值=更好。 - Decent Dabbler

    2

    MD5本身不安全,因为它部分被破解(碰撞)且摘要太小。如果您不想使用类似于bcrypt、scrypt或PBKDF2的适当密码派生函数,则至少应该在新设计中使用SHA-256(并制定计划以在SHA-3推出时进行迁移,因此请确保存储用于使用结果哈希密码的方案,使两个方案可以共存当人们更改密码时使用新的哈希过程)。

    如果您打算销售使用MD5的程序,则在任何方面使用MD5都可能成为大多数政府销售的障碍(例如在美国,使用的算法必须得到FIPS 140-2批准,许多其他国家也有同样的要求)。


    我一直很好奇,加盐密码会发生什么样的碰撞?(如果你真的找到了一个) - Your Common Sense
    从当前的知识角度来看,没有什么问题。碰撞攻击不一定转化为预像攻击,但它们的存在表明设计已经破裂。密码哈希函数契约的一部分已经破裂,因此您不能再信任它。继续使用MD5就像在有裂缝的玻璃杯中喝水,你可能完全没事,但它也可能在你手中破裂并送你去急诊室。 - Bruno Rohée
    我可以想象出玻璃破碎的情况。但是我不知道在哈希函数的情况下可能会发生什么特定的情况。你能详细说明一下吗?或者你不知道,只是为了以防万一而谨慎? - Your Common Sense
    小心谨慎,如果你有一堆杯子,为什么要喝有裂缝的那个,而旁边却有完好无损的。当一个加密哈希算法被攻破时,它的可信度就会瓦解。而且,换一个哈希算法通常只需要更改一个调用,为什么要选择次优的选项呢? - Bruno Rohée
    2
    只是成为一名程序员,而不是崇拜者。作为一名程序员,我想要理解,我的眼镜有什么特别的问题。 - Your Common Sense

    1

    通过您的解决方案,您基本上破坏了使用盐来防范预先计算字典攻击的目的。

    使用预先计算的字典表,正如其名称所示,某人已经提前为特定单词创建了哈希表(计算出的md5结果)。

    考虑这个哈希表hashtable(仅用于说明目的的想象哈希值)

    word | hash
    ------------
    foo  | 54a64
    bar  | 3dhc5
    baz  | efef3
    

    通过将这些值与您的表进行比较,可能只需要这样:

    SELECT h.word
    FROM hashtable h, yourtable y
    WHERE y.password = MD5( CONCAT( h.word, h.hash ) );
    

    如果有匹配,你就可以得到密码。

    然而,如果在将密码再次连接并哈希之前没有对其进行哈希处理,则使用预先计算的字典攻击它会更加困难。因为此时密码可能是md5('testtest'),这使得预先计算的表变得毫无价值,如果预先计算的表只考虑了单词的单个实例。

    您可以轻松地看出,如果您没有使用密码作为盐,而是使用另一个随机字符串作为盐,那么情况会变得更加困难。如果您为每个密码创建唯一的盐,那么情况会变得更加困难。当然,如果您为每个密码创建唯一的盐,您需要在数据库行中与密码一起保存盐。

    所以我的建议是:

    md5( 'uniquesalt' . 'password' );
    

    实际上,不要使用md5,而是使用更好的sha1sha256(或更高级别的)哈希算法。


    你的查询与计算每个可能变体的哈希值相同。它与暴力破解有何不同? - Your Common Sense
    @Col. Shrapnel:你说得很有道理。它确实没有太大的区别,除了你不必要进行两次哈希,这可能会为你节省一些时间。也许并不是很多。但我的主要观点是要说明预计算表可能通常只考虑单个单词。而不考虑被哈希的连接单词。 - Decent Dabbler

    1

    为什么建议使用随机密码盐来哈希密码,这样一个知道密码哈希值的攻击者就无法将其与彩虹表中预先计算出的字典哈希值进行比较。

    如果您使用密码作为盐,攻击者可以首先从他们的字典中预先计算$word.md5($word)的哈希值。


    预计算和计算本身有什么区别? - Your Common Sense

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