PHP 密码的安全哈希和盐

新手上路,请多包涵

目前据说MD5部分不安全。考虑到这一点,我想知道使用哪种机制来保护密码。

这个问题, “双重哈希”密码是否比仅哈希一次更安全? 建议多次散列可能是一个好主意,而 如何为单个文件实施密码保护? 建议使用盐。

我正在使用 PHP。我想要一个安全快速的密码加密系统。将密码散列一百万次可能更安全,但也更慢。如何在速度和安全之间取得良好的平衡?另外,我希望结果具有恒定数量的字符。

  1. 散列机制必须在 PHP 中可用
  2. 它必须是安全的
  3. 它可以使用盐(在这种情况下,所有盐都一样好吗?有什么方法可以产生好的盐吗?)

另外,我是否应该在数据库中存储两个字段(例如,一个使用 MD5,另一个使用 SHA)?它会让它更安全还是更不安全?

如果我不够清楚,我想知道要使用哪个散列函数以及如何选择好的盐,以便拥有安全快速的密码保护机制。

不完全涵盖我的问题的相关问题:

PHP中的SHA和MD5有什么区别

简单密码加密

为 asp.net 存储密钥、密码的安全方法

您将如何在 Tomcat 5.5 中实现加盐密码

原文由 luiscubal 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 723
2 个回答

免责声明:这个答案写于 2008 年。

从那时起,PHP 为我们提供了 password_hashpassword_verify 并且自推出以来,它们是推荐的密码散列和检查方法。

答案的理论仍然是一个很好的阅读。

TL;博士

不要做

  • 不要限制用户可以输入哪些字符作为密码。只有白痴才会这样做。
  • 不要限制密码的长度。如果您的用户想要一个带有 supercalifragilisticexpialidocious 的句子,请不要阻止他们使用它。
  • 不要去除或转义密码中的 HTML 和特殊字符。
  • 切勿以纯文本形式存储用户密码。
  • 永远不要通过电子邮件向您的用户发送密码, 除非他们丢失了密码,并且您发送了一个临时密码。
  • 永远不要以任何方式记录密码。
  • 切勿使用 SHA1 或 MD5 甚至 SHA256 散列密码! 现代破解器 可以超过 60 和 1800 亿哈希/秒(分别)。
  • 不要将 bcrypt 和 hash() 的 原始 输出 混合使用,要么使用十六进制输出,要么使用 base64_encode。 (这适用于任何可能包含流氓 \0 的输入,这会严重削弱安全性。)

多斯

  • 尽可能使用 scrypt; bcrypt 如果你不能。
  • 如果您不能使用 bcrypt 或 scrypt 以及 SHA2 哈希,请使用 PBKDF2。
  • 当数据库被破坏时重置每个人的密码。
  • 实现一个合理的 8-10 个字符的最小长度,加上至少需要 1 个大写字母、1 个小写字母、一个数字和一个符号。这将提高密码的熵,从而使其更难破解。 (有关辩论,请参阅“什么是好的密码?”部分。)

为什么要散列密码呢?

散列密码背后的目标很简单:通过破坏数据库来防止恶意访问用户帐户。因此,密码散列的目标是通过花费太多时间或金钱来计算纯文本密码来阻止黑客或破解者。时间/成本是您武器库中最好的威慑力量。

您希望用户帐户具有良好、强大的散列的另一个原因是给您足够的时间来更改系统中的所有密码。如果您的数据库受到威胁,您将需要足够的时间来 至少 锁定系统,如果不更改数据库中的每个密码的话。

Whitehat Security 的 CTO Jeremiah Grossman 在最近一次密码恢复需要暴力破解他的密码保护后 在 White Hat Security 博客上表示

有趣的是,在经历这场噩梦的过程中,我学到了很多我不知道的密码破解、存储和复杂性。 我开始明白为什么密码存储比密码复杂性重要得多。如果您不知道密码是如何存储的,那么您真正可以依赖的只是复杂性。 这可能是密码和加密专业人士的常识,但对于普通的 InfoSec 或 Web 安全专家来说,我非常怀疑。

(强调我的。)

什么才是 好的 密码?

。 (并不是说我完全赞同兰德尔的观点。)

简而言之,熵是密码中有多少变化。当密码只有小写罗马字母时,只有 26 个字符。那变化不大。字母数字密码更好,有 36 个字符。但是允许大小写,带符号,大约是 96 个字符。这比单纯的字母好多了。一个问题是,为了让我们的密码容易记住,我们插入了模式——这会降低熵。哎呀!

密码熵很容易 近似。使用全范围的 ascii 字符(大约 96 个可键入字符)会产生每个字符 6.6 的熵,对于未来的安全来说,8 个字符的密码仍然太低(52.679 位的熵)。但好消息是:更长的密码和带有 unicode 字符的密码,确实会增加密码的熵并使其更难破解。

Crypto StackExchange 网站上对密码熵进行了更长的讨论。一个好的谷歌搜索也会出现很多结果。

在我与@popnoodles 交谈的评论中,他指出使用 X 多个字母、数字、符号等 执行 X 长度的密码策略实际上可以通过使密码方案更可预测来减少熵。我同意。尽可能真正随机的随机性始终是最安全但最不令人难忘的解决方案。

据我所知,制作世界上最好的密码是 Catch-22。要么是难以记住、太容易预测、太短、太多 unicode 字符(在 Windows/Mobile 设备上难以输入)、太长等。没有任何密码能真正满足我们的目的,因此我们必须像保护它们一样保护它们在诺克斯堡。

最佳实践

Bcrypt 和 scrypt 是当前的最佳实践。 Scrypt 在时间上会比 bcrypt 更好,但它还没有被 Linux/Unix 或网络服务器作为标准采用,并且还没有发布对其算法的深入评论。但是,该算法的未来确实看起来很有希望。如果你正在使用 Ruby,有一个 scrypt gem 可以帮助你,Node.js 现在有它自己的 scrypt 包。您可以通过 Scrypt 扩展或 Libsodium 扩展(两者都在 PECL 中可用)在 PHP 中使用 Scrypt。

我强烈建议您阅读 crypt 函数 的文档,如果您想了解如何使用 bcrypt,或者为自己找到一个 好的 包装器 或使用 PHPASS 之 类的东西来实现更传统的实现。我建议至少 12 轮 bcrypt,如果不是 15 到 18 轮。

当我了解到 bcrypt 只使用带有可变成本机制的河豚密钥计划时,我改变了使用 bcrypt 的想法。后者让您通过增加河豚已经很昂贵的密钥计划来增加暴力破解密码的成本。

平均做法

我几乎无法再想象这种情况了。 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 这样昂贵的方案如此重要的原因。

您不可能预见到所有威胁或攻击途径,因此您必须尽最大努力预先保护您的 用户。如果你不这样做,那么你甚至可能会错过你被攻击的事实,直到为时已晚…… _你要承担责任_。为了避免这种情况,一开始就表现得偏执。攻击您自己的软件(内部)并尝试窃取用户凭据,或修改其他用户的帐户或访问他们的数据。如果您不测试系统的安全性,那么除了您自己之外,您不能责怪任何人。

最后:我不是密码学家。我所说的都是我的观点,但我碰巧认为它是基于良好的常识……以及大量阅读。请记住,尽可能多疑,让事情尽可能难以入侵,然后,如果您仍然担心,请联系白帽黑客或密码学家,看看他们对您的代码/系统的看法。

原文由 Robert K 发布,翻译遵循 CC BY-SA 4.0 许可协议

自 PHP 5.5 起,PHP 具有用于散列和验证密码的简单、安全的函数 password_hash()password_verify()

 $password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

当使用 password_hash() 时,它会生成一个随机盐并将其包含在输出的哈希中(以及使用的成本和算法。) password_verify() 然后读取该哈希并确定盐和使用的加密方法,并根据提供的明文密码对其进行验证。

提供 PASSWORD_DEFAULT 指示 PHP 使用 PHP 安装版本的默认哈希算法。确切地说,这意味着哪种算法打算在未来的版本中随着时间的推移而改变,因此它将始终是最强大的可用算法之一。

增加成本(默认为 10)使哈希更难暴力破解,但也意味着生成哈希并针对它们验证密码将为您的服务器的 CPU 做更多的工作。

请注意,即使默认的散列算法可能会改变,旧的散列将继续验证,因为使用的算法存储在散列中,并且 password_verify() 接受它。

原文由 AlliterativeAlice 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题