如何正确存储密码?

18

我在SO上偶然发现的article提供了指向other articles的链接,这些文章又提供了指向even more articles等更多的链接。

最终,我完全被难倒了 - 那么在数据库中存储密码的最佳方法是什么?从我所了解的,你应该:

  • 使用长(至少128位完全随机的)盐,它与密码一起以明文形式存储;
  • 对盐化密码使用几次SHA-256(甚至更高级别的SHA)迭代。

但是...越读有关密码学的内容,我就越明白我实际上什么也不懂,而我多年来认为正确的事情实际上是错的。这里有没有专家可以帮忙解答?

添加:似乎有些人没有理解重点。我重复上面给出的最后一个链接。这应该澄清我的疑虑。

https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2007/july/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/


你说得完全正确。不要存储密码,而是存储密码的哈希值。你真正的问题是什么?盐和哈希的哪个部分让你困惑了?还是因为这对你来说是新的而感到不满意?你找到了什么矛盾的建议? - S.Lott
2
没有“正确”的答案。安全级别很大程度上取决于应用程序的上下文环境。与网络没有物理连接的隔离计算机可以比面向Web的数据库具有更少的密码散列。再说,您的数据库永远不应直接可访问于网络,用户和数据库之间应始终存在访问层。如果以明文格式发送密码到数据库/访问层,则密码的哈希/盐值设定是无效的。 - Skizz
2
你说得对。我误解了重点。你定义了最佳实践。你的链接定义了最佳实践。每个人都说盐和哈希。你有什么问题?你不喜欢什么?你做不到什么?有什么问题? - S.Lott
从这篇文章中,我真的不理解“最佳”配方。提到了MD5和bcrypt;建议不要使用快速哈希函数;没有关于盐应该如何使用的建议(多长时间,哪里连接,如何存储等);总之,让我感觉自己对密码学一无所知,最好向专家寻求建议,而不是试图自己弄清楚事情。 - Vilx-
3
不要使用“几次”哈希迭代,而是使用“数千次”。 - erickson
显示剩余4条评论
6个回答

12

你理解得没错。只有两个建议:

  1. 如果有一天SHA1变得太弱了并且您想使用其他算法,就无法反向解析旧密码并重新用新方案加密它们。出于这个原因,我建议将每个密码附加一个“版本”号,告诉您使用的是什么方案(盐的长度、使用的哈希类型、循环次数等)。如果有一天需要从SHA切换到更安全的算法,您可以创建新式密码,同时仍然在数据库中保存旧式密码并区分它们。迁移用户到新方案将更容易。

  2. 密码仍然以未加密的形式从用户传输到系统。如果这是一个问题,请查看SRP。SRP如此新颖以至于您应该对其实施有些偏执,但目前看起来很有前途。

编辑:事实证明bcrypt已经在第一个想法上超越了我。存储的信息是(成本、盐、哈希值),其中成本是哈希运算的次数。看起来bcrypt做对了什么。增加哈希次数可以在不需要用户干预的情况下完成。


1
赞同为密码记录版本的想法。我可以看到这在未来会很有用... - Matt
1
这实际上是一个不好的想法,因为它通过解决尚不存在的问题而不必要地使解决方案变得过于复杂。这是违反YAGNI原则的典型例子。 - Chris Staley
1
+1 是为了解决真正的问题。Kiff 的概括否认很容易被 Stack Overflow 上关于此问题的实例所证明。作为一个不得不在短时间内要求数万用户选择新密码的人,我希望我能有一个更顺畅的迁移路径。 - erickson
4
@Kiff - 这与YAGNI有什么关系?曾经被认为是强大的哈希算法已经被证明是脆弱的(例如MD5),制定应对SHA1出现漏洞或变得过时的计划不是YAGNI,而是从历史中吸取教训。实际上,专家们已经开始质疑SHA1。 - Steve

2
实际上,这取决于密码的用途。存储任何密码都需要小心谨慎,但有时需要比其他情况更加小心谨慎。一般规则是所有密码都应该进行哈希处理,并且每个密码都应该有一个唯一的盐。
实际上,盐并不需要那么复杂,即使很小的盐也可以对试图进入系统的破解者造成真正的噩梦。它们被添加到密码中以防止使用 Rainbow 表来破解多个账户的密码。我不会只添加一个字母到密码中然后称之为盐,但你也不需要将其作为加密的唯一 guid 存储在数据库中。
关于盐的另一件事是,让密码+盐在哈希时工作的关键是两者组合的复杂性。如果您有一个 12 个字符的密码,并添加一个 1 个字符的盐,那么盐并没有什么作用,但破解密码仍然是一个巨大的壮举。反之亦然。

1

使用:

  1. 哈希密码存储
  2. 128位或以上的用户级盐值,随机生成(即,在生成新的密码哈希时制作新的盐值,不会为给定用户持久保留相同的盐值)
  3. 强大的、计算密集型的哈希方法
  4. 方法论与任何“标准实现指南”(例如这些指南)以及您编写的任何其他密码存储实现有所不同(包括哈希算法、使用多少哈希迭代、盐值连接的顺序等)

1
128位是过度杀伤力的。即使64位也是过度杀伤力的,所以这就是我使用的。除非“用户级盐”在某种程度上对用户来说是可预测的(比如使用他们的姓名),否则没有必要使用“系统级盐”。使用加密随机数生成器作为盐更容易和更安全。 - erickson
过度杀伤力?我喜欢过度杀伤力。系统级盐是为了防止在未泄露源代码的情况下,数据库内容被攻击者入侵而采取的深度防御措施。同意盐应该是噪音,而不是用户信息。 - chaos
我不明白。如果正确地完成,摘要、盐和源代码都可以被公开,但攻击者仍然无法发现密码。当你说数据库可能会受到威胁时,你是什么意思?你所说的“威胁”是什么意思? - erickson
创建一个给定密钥空间的新彩虹表比对该空间进行暴力搜索更昂贵。只有在你已经拥有正确的字典时,字典攻击才会快速。如果攻击者拥有盐值,你不会失去任何东西。这正是 OP 引用的文章的要点(两次):如果你仍然担心彩虹表,那就没有必要保护任何人的数据。 - erickson
4
说“如果攻击者掌握了你的盐值,那么它的作用就失去了”是不正确的。盐值应该是公开的信息。盐值有两个目的,一个是使密码包含一些真正随机的位,另一个是让两个相同的密码哈希为不同的值。 - Eyal
显示剩余2条评论

0

我认为密码不需要额外的迭代,只需确保有盐和复杂的盐;) 我个人使用SHA-1与2个盐关键短语相结合。


1
一遍哈希存在漏洞。您可以枚举一组非常可能导致哈希的候选项。多次通过会导致彩虹表迅速增长,因此需要256x哈希。http://en.wikipedia.org/wiki/Rainbow_table - S.Lott
我使用SHA-512,因为空间(和算法时间)不是问题,并且有一篇论文显示SHA-1存在碰撞...但我只是有点多疑。 - Kladskull
一次哈希是可笑的。至少使用1000,最好使用2000。 - erickson

0

盐的长度并不重要,只要它对于用户来说是唯一的就可以了。盐的原因是为了让生成的哈希匹配仅对数据库中用户表的单行有用。


3
很重要。你的盐所起到的作用是增加彩虹表破解哈希值时的大小。如果盐很短,其作用就不大。(盐主要解决的问题之一是用户选择了短密码。)请注意,此翻译力求通俗易懂并保留原意,不包含除翻译结��外的任何其他内容。 - chaos
它确实很重要,但只有到一定程度。每添加一点盐,都会使词典攻击所需的空间要求加倍(或者攻击者必须将其词典减半)。如果有一个良好的密码选择策略(最小长度,要求混合大小写,数字,标点符号),则使用旧的12位盐可能就足够安全了。然而,最好使用64位并在任何地方保持安全。即使它们非常微小,也没有人真正有足够的空间来存储10^19个词典。 - erickson

0
简单来说,对于密码,使用一个加密安全的哈希算法和一些盐,这应该足以满足99.99%的所有使用情况。弱点将是检查密码的代码以及密码输入。

只要提供更好的安全性只需要增加5行简单的代码,我想在所有使用情况下选择更好的选项。 - Vilx-
我不认为你能在“另外五行琐碎的代码”中得到那个...但我也会阅读其他答案和评论,也许他们会证明我错了。 - Lucero

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