在数据库中加密用户密码的推荐方法是什么?

17
在一个使用Perl和PostgreSQL编写的Web应用程序中,用户有用户名和密码。存储密码的推荐方法是什么?
使用Perl的crypt()函数和随机盐对它们进行加密?这将限制密码的有效长度为8个字符,并且需要获取存储的密码以便与用户提供的密码进行比较(以获取附加的盐)。
PostgreSQL中是否有内置的方法来完成此操作?
我应该使用Digest::MD5吗?
7个回答

29

不要使用SHA1或SHA256,尽管大多数人建议使用它们。 绝对不要使用MD5

SHA1/256和MD5都是设计用来创建文件和字符串(以及必要时的其他数据类型)的校验和。由于这个原因,它们被设计成尽可能快,以便快速生成校验和。

这种快速性使得暴力破解密码变得更容易,因为一个写得好的程序可以轻松地每秒生成数千个哈希值。

相反,应该使用一种专门为密码设计的缓慢算法。它们被设计成要花费更长的时间来生成,好处是暴力攻击变得更加困难。因此,密码将更加安全。

如果你只是单独加密密码,即存储和检查密码的常规实现方式,你不会遇到任何显著的性能劣势。真正的差别只有在批量处理时才会体现。

我个人喜欢bcrypt。应该有Perl版本可供使用,通过快速的谷歌搜索可以找到几个可能的匹配项。


1
你根本不知道自己在说什么,远离安全标签。消息摘要算法旨在快速摘要数据并尝试使其无法逆转。NIST不会批准慢的加密算法。 - rook
1
这纯粹是胡说八道。哈希算法分为两种,加密哈希和非加密哈希。SHA和MD5都是加密哈希。回想一下SSL / TLS协议,整个方案的基础是你无法找到这些哈希值的冲突。所以如果您认为它不安全,最好停止使用https。这就是MD5被破解的原因,因为使用300个playstation可以找到冲突。 SHA和MD5唯一已知的攻击是彩虹表攻击,这也适用于bcrypt。因此,由于这种胡说八道,票数应该很低。因为"谷歌这么说"是个不好的争论点。 - Henri
10
Henri,你还是没有理解我的观点。就像我不是在谈论反转一样,我也不是在谈论碰撞。无论你使用什么加密算法,只要拥有足够的时间和CPU,就可以找出任何密码。这就是为什么bcrypt比sha1或md5更好的地方,因为生成时间更长。在一个简单的脚本中,我用bcrypt对“我的密码”进行了1000次加密,而用sha1进行了1000次加密。bcrypt花费了96.3秒,而sha1则小于0.01秒。祝你好运,使用一个慢10,000倍的算法来填充那些彩虹表。 - vonconrad
2
此外,我发现有趣的是,除了你和未知人的负面评价之外,我的回答仍然有四个赞。而Dave Sherohman则与我完全一样的论点(尽管稍微更加外交),却得到了至少七个赞。也许我/我们所说的话确实有一些价值? - vonconrad
1
那么,为什么bcrypt比迭代sha256一百万次更好呢? - b0fh
显示剩余2条评论

17

MD5通常被使用,但SHA1/SHA256更好。它们仍然不是最佳选择,但比MD5更安全。

所有通用哈希算法的问题在于它们被优化为快速处理。但是,当您将密码哈希值存储时,快速处理并不是您想要的——如果您可以在微秒内哈希密码,则表示攻击者能够尝试每秒一百万个密码,如果他们获得了您的密码数据库。

但您肯定想尽可能地减缓攻击者的速度,对吧?那么使用需要花费十分之一秒来哈希密码的算法是否更好呢?十分之一秒的速度足够快,以至于用户通常不会注意到,但是拥有您数据库副本的攻击者每秒只能尝试10个密码,寻找可用的登录凭据将需要比使用微秒每次尝试还多100,000倍的时间。使用微秒每次尝试的每小时成本变为使用十分之一秒每次尝试的11年。

那么,如何实现这一点呢?有些人通过运行多轮MD5/SHA摘要来模拟,但bcrypt算法是专门设计来解决此问题的。我并不完全理解其背后的数学原理,但我被告知它基于Blowfish帧的创建,这本质上是缓慢的(不像MD5操作可以在正确配置的硬件上大幅简化),并且它具有可调整的“成本”参数,因此,随着摩尔定律的推进,您只需要调整该“成本”来保持密码哈希值在十年后与今天一样慢。


+1 表示采用更安全的方法所需的额外时间通常不会被最终用户察觉。 - coder_tim

3
我最喜欢bcrypt,其次是SHA2(256)。我从未见过MD5用于密码,但某些应用程序/库可能使用它。请记住,您始终应该使用盐。盐本身对于每个用户都应该是完全独特的,并且在我看来越长越好。我绝不会只使用散列来处理字符串而没有添加盐,主要是因为我有点偏执,同时这样也更加具备未来性。

在用户再次尝试之前设置延迟以及自动锁定(并带有自动管理员通知)也是一个好主意。


2

我最近实现了pgcrypto,但在数据库上让它正常工作相当麻烦(你必须非常小心使用它的查询,如果以某些方式格式化它们,可能会导致执行速度变慢)。你必须安装模块,以root身份运行脚本,然后才能使用crypt。虽然它很安全,但添加起来并不容易。如果您有一个好的代码级哈希算法,那可能是一个不错的替代方案。 - Kzqai

0

使用加盐的SHA1或SHA256哈希方式。这是存储密码的最佳方法。


1
还要记得给你的密码加盐。http://www.aspheute.com/english/20040105.asp - Dan Herbert
请确保您不需要支持基于哈希的身份验证方案。由于新要求需要支持LDAP {SSHA}方案,我对在数据库中哈希密码的决定感到遗憾。 - ZZ Coder
2
@ZZ Coder:那有什么问题吗?只需创建您的新集合,当用户登录时,您可以在新数据库中进行一些幕后魔法来升级她。您绝不能存储可以以明文检索的密码,散列是一种很好的方法。 - Leonardo Herrera
10
坏主意。请查看此帖子中与暴力攻击密码速度相关的其他帖子。 - dland
@dland,看一下我的评论。速度真的不是问题。 - Henri
11
bcrypt是你应该使用的加密方式,而不是MD5/SHA1/SHA256或其他通用的哈希函数。请查看这篇文章以获得更详细的解释。 - Sam Choukri

0

如果您不使用密码恢复机制(而非密码重置),我认为使用哈希机制比尝试加密密码更好。您可以在没有任何安全风险的情况下检查哈希值。即使您不知道用户的密码。


1
考虑到他提到了Perl的crypt()函数,该函数“创建一个与C库中的crypt(3)函数完全相同的摘要字符串”,我怀疑他/她知道应该使用哈希机制,只是使用了错误的术语。 - David Precious

-3
我建议将其存储为加盐的MD5哈希值。
INSERT INTO user (password) VALUES (md5('some_salt'||'the_password'));

如果你想的话,你可以在perl中计算md5哈希值,除非你正在微调,否则没有太大区别。

你也可以使用sha1作为替代方案,但我不确定Postgres是否有本地实现。

我通常不鼓励使用动态随机盐,因为这是必须存储在数据库中的另一个字段。而且,如果您的表被攻击,盐将变得无用。

我总是使用一次性随机生成的盐,并将其存储在应用程序源或配置文件中。

使用md5或sha1哈希来存储密码的另一个好处是,您可以将密码列定义为固定宽度的CHAR(32)或CHAR(40)。


2
MD5 已经被认为是不安全的一段时间了。应该使用 SHA1 或 SHA256。 - EKS
4
嗯,我也读过那些博客文章。对于90%(随意数字)的使用情况,MD5 已经足够了。 - hobodave
1
@Henri:没错,但是关于他的安全性、存储和计算能力需求方面没有提供任何证据。作为一个非本地的Postgres用户,我只是在手册中搜索了一下,发现它有原生的MD5但没有SHA1,因此我建议使用MD5。我的建议可能需要这些评论,我同意,但是负评?嗯,似乎有点过分了。 - hobodave
2
MD5可能已经被破解了,但是攻击(由王小云和余洪波提出的)并没有针对加盐的MD5情况。它假定完全自由地选择第二个输入。因此,该提案是合理的,而且不容易受到攻击。我唯一要做的就是使用一个不太平凡的加盐算法。即使是将密码长度附加到MD5输入中这样简单的做法也可以极大地复杂化攻击。 - MSalters
3
如果你的数据库被攻击,盐绝对不会变得无用 - 实际上,这是它提供好处的唯一时机。如果不对密码进行加盐(或为每个用户使用相同的盐),则攻击者可以立即确定两个用户是否使用相同的密码。未使用盐还会使你更容易受到查找彩虹表攻击的威胁,而不需要攻击者实际上花费时间计算哈希值以找到匹配项。 - Dave Sherohman
显示剩余7条评论

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