如何使用PhP加密存储在MySql中的密码的最佳实践是什么?

16

我正在寻求有关如何使用PHP在MySQL中 安全地 存储密码的建议。

忽略PHP本身的限制,我想了解更多关于加盐、哈希和加密这些密码的信息。

明显的是,除非被强制要求,否则人们将继续使用弱密码,但对我来说,重要的是我如何存储它们。我的用户密码对我来说比数据库本身更重要,因此我希望以这样一种方式保持它们,以便任何尝试反向操作的脚本小子都会感到痛苦和乏味。显然,只要尽职尽责,几乎任何事情都可以被打败,但我不介意把这件事变得特别麻烦。

我们正在考虑两种情况。

  1. 那个小子拥有完整的数据库副本。
  2. 那个小子拥有完整的用于制作密码的PHP以及数据库。

非常感谢关于这个主题的所有建议。


1
最好的方法是根本不使用密码。请参考InstaPaper。 - user142019
你的用户是本地的还是通过互联网连接的? - Raj More
本地和互联网两者都可以。我更喜欢不依赖于互联网资源,但我认为这也是一个选择(例如,从第三方提供商获取盐值)。 - user549167
这个问题在stackoverflow上已经被回答了将近一千次。 - Your Common Sense
1
我只是想澄清第一条评论。InstaPaper最近放弃了他们不需要密码的想法。现在在InstaPaper上需要密码,更多信息请阅读这里http://blog.instapaper.com/post/2318776738。 - Brian Boatright
6个回答

17

使用bcrypt加密密码。如果有人拥有您数据库的用户表,那么他们可以毫无顾忌地使用暴力破解/彩虹表等方法。即使使用了盐值,如果您使用MD5或其他快速哈希算法(它们顺便说一下并不是为了解决这个问题而设计的),它也只是一个时间问题,就会被破解。

任何广为人知和广泛支持的哈希算法都将具有相同的基本“缺陷”(如果您愿意称之为这样的话;实际上这是根据定义的)。不同之处在于,bcrypt在执行哈希操作时非常,从而使暴力破解攻击变得不太有效。

关于bcrypt的优点、其他方法的危害以及密码安全的困难程度的绝佳讨论,请阅读此主题。它有许多比我更了解这种事情的人发表的评论,希望能帮助您更好地理解所涉及的问题。


没错,bcrypt的优点在于您可以任意减慢哈希过程。如果需要将密码哈希化需要花费四分之一秒钟,对于您的服务器来说并不是什么大问题。但是,如果黑客只能每秒计算四个哈希值而不是数千个,则这对他们来说非常重要。 - eaj
@时间机器 哈哈,我想在我打完其他答案之前把它放出来 :) - Donut
如果你真的不在乎时间,或者你的服务器是一台超级计算机:只需将“工作因子”设置为10000。 - user142019
1
@Alisso:这是链接:https://news.ycombinator.com/item?id=2004833 我也已经更新了答案,加入了新的链接。 - Donut
不确定为什么这个答案被接受了。bcrypt 是哈希,而不是加密。OP 要求的是加密。 - metamonkey

8
假设您正在使用用户名和密码作为身份验证令牌,您可以安全地存储以下内容以确保数据不会被破坏。
- 用户名(明文) - 盐(随机字符串) - 加盐哈希(sha1(username + salt + password))
使用此方案,攻击者无法使用rainbow tables对您进行攻击,并且密码也无法通过任何(合理的)手段恢复。(也就是说,只要你的攻击者不是政府)
即使攻击者拥有盐和哈希值,也无法使用彩虹表,因为所有可能的哈希都需要使用他们得到的盐进行计算,所以对于每个用户来说,这是一个全新的暴力攻击。
即使有源代码,攻击者也无法获取密码,因为强度/安全性在哈希算法中,而不在您的代码中。

结合使用 Donut 的回答中提到的 bcrypt,您会变得非常安全。也就是说:

  • 用户名(明文)
  • (随机字符串)
  • 加盐哈希值bcrypt(用户名 + 盐 + 密码))

+1:有一个警告。 “任何手段”..并不完全是这样。您仍然可以构建彩虹表并找到碰撞。但是,您需要为每个盐制作一个。虽然可能,但只是不可行的(因此+1)。如果您是NSA或其他政府机构,则所有赌注都关闭。 - NotMe
@Matthew Salt(随机字符串) 对于所有用户是相同还是不同的?如果是不同的,那么我们需要将其存储在数据库中吗? - MeVimalkumar
每个用户使用不同的盐最好。 - Matt

3
根据这里的建议,为了增加乐趣,您还可以动态更改salt。例如,对于不同长度的用户名使用不同的salt,使用用户的注册日期作为salt。这样即使有人成功进入您的数据库,他们也不能仅仅重新生成哈希值,而是必须为每个使用的salt计算哈希表。

这是该主题的一个很好的补充。我在一堂安全课上听到过这个讨论。实际上,你只需要每次添加一个用户时将盐值增加1。只要每个用户的盐值都不同,彩虹表就必须为每个用户重新计算,使暴力攻击变得极其烦人。+1 - Nathan Garabedian
1
每个考虑将密码存储在数据库中的人都应该阅读这篇博客文章。 - niteria

1
通常,“加盐”密码(例如使用bcrypt)意味着不仅存储密码本身,而是只存储类似于的内容。
   salt
   hash(salt with password appended)

现在,如果某个小孩有你的数据库(当然还有代码-保留代码不是明智之举),他/她只能猜测密码、计算盐值哈希并进行比较。如果哈希函数很耗费资源(例如bcrypt),那么猜测密码也会变得非常耗费资源。

1

假设他们拥有数据库/PHP,他们很清楚盐的作用。强制破解只需要将盐添加到密码前面即可。 - user549167
您可以对PHP代码进行字典攻击,因为它已经包含了将密码字典转换为带盐哈希所需的代码。换句话说,如果我有数据库和PHP代码,我会使用该代码对数据库运行字典攻击。PHP代码将使用带盐哈希来检查每个密码是否与数据库匹配。 - Nathan Garabedian
@aqfire: stored = hash(salt + pass)。字典中可能包含某些输入,导致特定的哈希结果,但它永远不会是加盐的密码。如果盐足够强,invhash(stored).startsWith(salt) == false - Bart van Heukelom
@aqfire:我认为字典不是这样工作的。假设我的密码是bar,盐值是foofoo,生成的哈希值是1234。然而,将字符串php进行哈希也会返回1234。现在,假设foofoo是一个长且唯一的盐值,那么dict(1234)返回php的可能性要比返回foofoobar大得多。而且,没有密码可以附加到foofoo以形成php - Bart van Heukelom
@bart,你说的是维基百科页面(http://en.wikipedia.org/wiki/Dictionary_attack)中所称的“预先计算字典攻击”,其中字典单词直接转换为哈希码,但这并不是进行字典攻击的唯一方式。它们之所以如此危险,是因为如果你有一个小字典和足够的时间,你可以编写一个脚本来尝试所有字典单词对登录/密码提示进行攻击,而盐在这种情况下无济于事。 - Nathan Garabedian
显示剩余3条评论

1
如果您的用户在互联网上,OpenId 将是您最好的选择之一。http://openid.net/ 如果您的用户在您的网络上,您可以使用集成安全吗?
换句话说,不要存储他们的密码。

它们可能在互联网上,有时也可能是本地的。因此我不能依赖OpenID。谢谢! - user549167

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