协慌网

登录 贡献 社区

为 PHP 密码保护哈希和盐

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

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

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

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

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

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

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

PHP 中 SHA 和 MD5 的区别是什么
简单的密码加密
安全的方法存储密钥,asp.net 的密码
您将如何在 Tomcat 5.5 中实现 salted 密码

答案

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

从那以后,PHP 给了我们password_hashpassword_verify ,自从他们介绍之后,他们就是推荐的密码哈希和检查方法。

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

TL; DR

注意事项

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

DOS

  • 尽可能使用 scrypt; bcrypt,如果你不能。
  • 如果您不能使用带有 SHA2 哈希值的 bcrypt 或 scrypt,请使用 PBKDF2。
  • 在数据库受到威胁时重置每个人的密码。
  • 实现合理的 8-10 个字符的最小长度,另外需要至少 1 个大写字母,1 个小写字母,数字和符号。这将改善密码的熵,从而使其更难破解。 (请参阅 “什么是一个好的密码?” 部分进行一些辩论。)

为什么哈希密码呢?

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

您希望在用户帐户上使用良好,健壮的哈希的另一个原因是为您提供足够的时间来更改系统中的所有密码。如果您的数据库受到威胁,您将需要足够的时间来至少锁定系统,如果不更改数据库中的每个密码。

Whitehat Security 的首席技术官 Jeremiah Grossman 在最近的密码恢复后在他的博客上表示,他需要暴力破解他的密码保护:

有趣的是,在实现这个噩梦的过程中,我学到了很多关于密码破解,存储和复杂性的知识。 我开始意识到为什么密码存储比密码复杂性更重要。如果您不知道密码的存储方式,那么您真正依赖的就是复杂性。这可能是密码和加密专业人员的常识,但对于普通的 InfoSec 或 Web 安全专家,我非常怀疑。

(强调我的。)

是什么让一个好的密码?

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

简而言之,熵是密码内的变化程度。当密码只是小写罗马字母时,那只有 26 个字符。这没有太大的变化。字母数字密码更好,有 36 个字符。但允许使用符号的大写和小写大约是 96 个字符。这比信件要好得多。一个问题是,为了使我们的密码令人难忘,我们插入模式 - 这减少了熵。哎呀!

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

Crypto StackExchange站点上有一个关于密码熵的更长时间的讨论。良好的 Google 搜索也会带来很多结果。

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

据我所知,制作世界上最好的密码是 Catch-22。它不是难忘的,太可预测的,太短的,太多的 unicode 字符(难以在 Windows / 移动设备上键入),太长时间等等。没有密码对我们的目的来说真的足够好,所以我们必须像他们一样保护它们在诺克斯堡。

最佳做法

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

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

当我得知 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 这样昂贵的计划如此重要的原因。

你不可能预见攻击的所有威胁或途径,所以你必须保护用户前面你最大的努力。如果你不这样做,那么你甚至可能会错过这样一个事实:你被攻击直到为时已晚...... 而且你有责任 。为了避免这种情况,首先要采取偏执行为。攻击您自己的软件(内部)并尝试窃取用户凭据,或修改其他用户的帐户或访问其数据。如果你不测试系统的安全性,那么除了你自己,你不能责怪任何人。

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

一个更短更安全的答案 - 根本不编写自己的密码机制 ,使用经过试验和测试的机制。

  • PHP 5.5 或更高版本: password_hash()质量很好,是 PHP 核心的一部分。
  • 较旧的 PHP 版本:OpenWall 的phpass库比大多数自定义代码要好得多 - 在 WordPress,Drupal 等中使用。

大多数程序员都没有专业知识来安全地编写加密相关代码而不会引入漏洞。

快速自检:什么是密码拉伸以及您应该使用多少次迭代?如果你不知道答案,你应该使用password_hash() ,因为密码机制的一个关键特性是密码扩展,因为 CPU 速度更快,并且使用GPU 和 FPGA每秒数十亿的猜测率破解密码(使用 GPU)。

例如,您可以使用 5 台台式机中安装的 25 个 GPU 在 6 小时内破解所有 8 个字符的 Windows 密码 。这是强制性的,即枚举和检查每个 8 个字符的 Windows 密码 ,包括特殊字符,并且不是字典攻击。那是在 2012 年,从 2018 年开始,您可以使用更少的 GPU,或者使用 25 个 GPU 更快地破解。

对于在普通 CPU 上运行且非常快的 Windows 密码,还有许多彩虹表攻击。所有这一切都是因为即使在 Windows 10 中 ,Windows 仍然 不会对其密码进行限制或扩展 - 不要像微软那样犯同样的错误!

也可以看看:

  • 优秀的答案 ,更多关于为什么password_hash()phpass是最好的方式。
  • 好的博客文章给出了主要算法的推荐 '工作因素'(迭代次数),包括 bcrypt,scrypt 和 PBKDF2。

我不会以两种不同的方式存储密码哈希,因为那时系统至少与使用中最弱的哈希算法一样弱。