免责声明:本答案是在2008年编写的。
自那时以来,PHP给我们提供了
password_hash
和
password_verify
,自从它们被引入以来,它们是推荐的密码哈希和检查方法。
尽管如此,这个答案的理论仍然值得一读。
简短概述
不要做的事情
- 不要限制用户输入密码的字符。只有蠢货才会这样做。
- 不要限制密码的长度。如果您的用户想用supercalifragilisticexpialidocious这个词,不要阻止他们使用。
- 不要剥离或转义密码中的HTML和特殊字符。
- 永远不要以明文形式存储用户的密码。
- 除非用户遗失密码并且您发送了临时密码,否则永远不要通过电子邮件发送密码给用户。
- 永远不要以任何方式记录密码。
- 永远不要使用SHA1或MD5甚至SHA256来哈希密码!现代破解工具可以超过60亿和180亿次哈希/秒(分别)。
- 不要混合bcrypt和hash()的原始输出, 要么使用十六进制输出,要么base64_encode它。(这适用于任何可能包含恶意
\0
的输入,这可能会严重削弱安全性。)
Do's
- 如果可以,请使用scrypt;如果不能,请使用bcrypt。
- 如果无法使用bcrypt或scrypt,请使用带有SHA2哈希的PBKDF2。
- 当数据库被攻击时,请重置所有用户密码。
- 实现合理的8-10个字符最小长度,同时要求至少1个大写字母、1个小写字母、一个数字和一个符号。这将提高密码的熵,从而使其更难破解。(请参见“什么是好密码?”部分进行一些辩论。)
为什么要哈希密码?
哈希密码背后的目标很简单:通过破坏数据库来防止恶意访问用户帐户。因此,密码哈希的目标是通过花费太多时间或金钱来计算明文密码来阻止黑客或破解者。时间/成本是您武器库中最好的威慑因素。
另一个你想要在用户帐户上有一个好的、强大的哈希的原因是给你足够的时间来改变系统中的所有密码。如果您的数据库被攻击,您需要足够的时间来至少锁定系统,如果可能,更换数据库中的每个密码。
Jeremiah Grossman,Whitehat Security的CTO,在最近一次需要强制破解他的密码保护的密码恢复之后在White Hat Security博客上说:
有趣的是,在经历这个噩梦时,我学到了很多关于密码破解、存储和复杂性的知识。我开始认识到为什么密码存储比密码复杂性更重要。如果你不知道你的密码是如何存储的,那么你所依赖的只是复杂性。对于密码和加密专家来说,这可能是常识,但对于普通信息安全或网络安全专家来说,我非常怀疑。
(强调是我的。)
好的密码应该具有
熵。 (虽然我并不完全认同Randall的观点。)
简而言之,熵是密码中包含多少变化。当一个密码只有小写罗马字母时,只有26个字符。这并不是很多变化。字母数字密码更好,有36个字符。但允许使用大写和小写字母以及符号,大约有96个字符。这比仅使用字母要好得多。问题之一是,为了使密码易记,我们会插入模式,从而降低熵。哎呀!
密码熵可以
轻松近似计算。使用ascii字符的全部范围(大约96个可输入字符)可产生每个字符6.6的熵,对于8个字符的密码来说仍然太低(52.679比特的熵)以保证未来的安全性。但好消息是:更长的密码和带有Unicode字符的密码确实增加了密码的熵,使其更难破解。
在Crypto StackExchange网站上有关于密码熵的更长讨论。良好的Google搜索也会得到很多结果。
在评论中,我与@popnoodles交谈,他指出强制使用X个字母、数字、符号等长度为X的密码策略实际上会通过使密码方案更加可预测来降低熵。我同意。随机性越真实的解决方案总是最安全但记忆最差的解决方案。
就我所知,创造世界上最好的密码是一个进退两难的局面。它要么不容易记忆,太容易预测,太短,太多Unicode字符(在Windows/Mobile设备上难以输入),太长等等。没有任何一个密码对我们的目的来说是完全足够的,因此我们必须像保护金库一样保护它们。
最佳实践
Bcrypt和scrypt是目前的最佳实践。 Scrypt将来会比bcrypt更好,但它还没有被Linux / Unix或Web服务器采用为标准,并且还没有对其算法进行深入审查。但未来的算法看起来很有前途。如果您正在使用Ruby,则有一个scrypt gem可以帮助您,Node.js现在有自己的scrypt包。您可以通过Scrypt扩展或Libsodium扩展(两者都可在PECL中获得)在PHP中使用Scrypt。
我建议阅读
crypt函数的文档以了解如何使用bcrypt,或者找到一个
好的 包装器或使用类似于
PHPASS的更传统的实现。如果不是15至18轮,则建议至少使用12轮bcrypt。
当我了解到bcrypt仅使用blowfish的密钥计划和可变成本机制时,我改变了对使用bcrypt的想法。后者使您可以通过增加blowfish已经昂贵的密钥计划来增加暴力破解密码的成本。
平均实践
我几乎无法再想象这种情况了。
PHPASS支持PHP 3.0.18到5.3,因此几乎可以在任何可想象的安装上使用 - 如果您不确定您的环境是否支持bcrypt,应该使用它。
但是假设您根本无法使用bcrypt或PHPASS。那么怎么办?
尝试使用
PDKBF2的实现,其
最大轮数应该与您的环境/应用程序/用户感知能够承受的范围相符。我建议的最低轮数是2500轮。此外,请确保使用
hash_hmac()(如果可用)使操作更难以复制。
未来实践
PHP 5.5中即将推出一个全面的密码保护库,它抽象了使用bcrypt时的任何痛苦。虽然我们大多数人在常见环境中仍然使用PHP 5.2和5.3,特别是共享主机,但@ircmaxell已经为即将推出的API构建了一个兼容层,向后兼容到PHP 5.3.7。
密码学回顾和免责声明
计算机需要的计算能力来实际“破解”一个散列密码是不存在的。计算机“破解”密码的唯一方法就是重新创建它并模拟用于保护它的哈希算法。哈希速度与其被暴力破解的能力成线性关系。更糟糕的是,大多数哈希算法都可以轻松并行化以加快速度。这就是为什么像bcrypt和scrypt这样昂贵的方案如此重要。
你不可能预见所有的威胁或攻击途径,因此你必须尽最大努力保护你的用户。如果你不这样做,甚至可能错过你被攻击的事实,直到为时已晚......而你会有责任。为了避免这种情况,从一开始就要表现得多疑。攻击你自己的软件(内部)并尝试窃取用户凭据,或修改其他用户的帐户或访问其数据。如果你不测试系统的安全性,那么你只能责怪自己。
最后:我不是密码学家。我说的任何话都是我的观点,但我碰巧认为这是基于老套的常识...和大量的阅读。记住,尽可能地多疑,让事情尽可能难以侵入,然后,如果你仍然担心,联系一个白帽黑客或密码学家看看他们对你的代码/系统有何建议。