【问题标题】:Secure hash and salt for PHP passwordsPHP 密码的安全哈希和盐
【发布时间】:2010-09-28 22:27:33
【问题描述】:

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

这个问题,Is “double hashing” a password less secure than just hashing it once? 建议多次散列可能是个好主意,而How to implement password protection for individual files? 建议使用盐。

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

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

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

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

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

What's the difference between SHA and MD5 in PHP
Simple Password Encryption
Secure methods of storing keys, passwords for asp.net
How would you implement salted passwords in Tomcat 5.5

【问题讨论】:

  • openwall.com/phpass 也是很好的库
  • Md5 现在完全不安全
  • @NSAwesomeGuy 这取决于您使用它的目的。彩虹匹配或只是暴力破解未加盐的 MD5 密码是微不足道的,当然,但是通过适当的盐渍,建立一个快速破解密码集的彩虹表仍然非常不切实际,而且暴力破解是不可能的。
  • PHP 5.5+ 在php.net/manual/en/function.password-hash.php 中内置了一个安全密码哈希

标签: php security passwords hash protection


【解决方案1】:

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

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

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

TL;DR

注意事项

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

Dos

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

为什么还要哈希密码?

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

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

Jeremiah Grossman,Whitehat Security 的首席技术官,stated on White Hat Security blog 在最近一次需要暴力破解密码保护的密码恢复后:

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

(强调我的。)

什么才是好的密码?

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

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

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

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

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

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

最佳做法

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

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

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

平均做法

我几乎无法再想象这种情况了。 PHPASS 支持 PHP 3.0.18 到 5.3,因此几乎可以在所有可以想象的安装中使用它,如果您确定您的环境支持 bcrypt,则应该使用它。

但是假设你根本不能使用 bcrypt 或 PHPASS。然后呢?

尝试使用您的环境/应用程序/用户感知可以容忍的maximum number of rounds 实现PDKBF2。我推荐的最低数量是 2500 发。此外,请确保使用hash_hmac(),如果它可以使操作更难重现。

未来实践

在 PHP 5.5 中引入了 full password protection library,它消除了使用 bcrypt 的任何痛苦。虽然我们大多数人在最常见的环境中(尤其是共享主机)都坚持使用 PHP 5.2 和 5.3,但@ircmaxell 为即将到来的 API 构建了一个 compatibility layer,它向后兼容 PHP 5.3.7。

密码学回顾和免责声明

实际上破解散列密码所需的计算能力并不存在。计算机“破解”密码的唯一方法是重新创建密码并模拟用于保护密码的哈希算法。散列的速度与其被暴力破解的能力呈线性关系。更糟糕的是,大多数哈希算法可以轻松并行化以更快地执行。这就是为什么像 bcrypt 和 scrypt 这样昂贵的方案如此重要的原因。

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

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

【讨论】:

  • 秘密无济于事,因为您的密码数据库无论如何都应该是秘密的 - 如果他们能掌握该数据库,他们也可以找到您正在使用的任何秘密。然而重要的是盐是随机的。
  • @wicked 跳蚤,我不是在和你争论。只是指出我们工作的这个领域是多么令人费解和复杂。我一直希望通过建立小型网站的内容管理系统的最聪明、最聪明的最佳实践来学习。我还在这儿学习。 ...每次我读到有意义的东西时,我很快就会注意到其他 5 篇与之相矛盾的帖子。那一圈又一圈很快就让人头晕目眩:)
  • 有趣的修订。用户 ID(例如,自动增量 BIGINT)是一个好的随机数吗?还是因为它不是随机的所以不好?此外,我必须将每个用户的随机数存储在数据库中......站点密钥 + 随机数 + HMAC 是否比多次迭代的加盐(使用用户 ID)哈希提供了显着改进的安全性?同样,多次迭代 HMAC 对安全性有好处吗?
  • 通过电子邮件发送临时密码,要求用户在首次使用时更改密码,并通过电子邮件发送“安全”链接以允许他们设置密码,这同样有风险。在任何一种情况下,只要在目标收件人之前使用链接或密码,任何拦截电子邮件的人都可以访问该帐户。
  • @RobertK 通过扩展字符集是的,它增加了,但是通过强制所有密码遵循规则减少了可能的选项数量。假设有人要通过蛮力获取密码。通过告诉他们用户的密码有 1 个大写字母、1 个小写字母、一个数字和一个符号,这意味着他们需要的尝试次数明显减少。通过允许用户决定他们想要什么,黑客必须更加努力。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我通常使用带有用户 ID(或其他一些用户特定信息)的 SHA1 和 salt,有时我还使用常量 salt(所以我有 2 个部分的 salt)。

    SHA1 现在也被认为有所妥协,但程度远低于 MD5。通过使用盐(任何盐),您可以防止使用通用 rainbow table 来攻击您的哈希(有些人甚至通过搜索哈希成功地将 Google 用作一种彩虹表)。可以想象,攻击者可以使用您的 salt 生成彩虹表,因此您应该包含用户特定的 salt。这样,他们将不得不为系统中的每条记录生成一个彩虹表,而不仅仅是为整个系统生成一个彩虹表!使用这种加盐方式,即使是 MD5 也相当安全。

    【讨论】:

    • 恒定盐不是一个好主意...可能不是一个致命的缺陷,但它不必要地削弱了计划。
    • MD5 和 SHA1 速度很快,所以这是一个糟糕的死机。
    【解决方案4】:

    Google 表示 SHA256 可用于 PHP。

    您绝对应该使用盐。我建议使用随机字节(而不是限制自己使用字符和数字)。通常,您选择的时间越长,它就越安全,速度越慢。我猜 64 字节应该没问题。

    【讨论】:

    • 64 位应该对任何人都足够了吗?
    • @Konerak,我会在 20 年后回到这个话题。 :) 但是是的,SHA256 确实可用。如果您想知道 SHA256 的安全性,您可能需要查看以下内容:security.stackexchange.com/questions/90064/…
    【解决方案5】:
    在可预见的未来,

    SHA1 和盐应该就足够了(当然,这取决于您是为Fort Knox 编写代码还是为您的购物清单编写登录系统)。如果 SHA1 对您来说不够好,请使用 SHA256

    盐的想法是使散列结果失去平衡,可以这么说。例如,已知空字符串的 MD5 散列是d41d8cd98f00b204e9800998ecf8427e。因此,如果有足够好的记忆力的人会看到该散列并知道它是空字符串的散列。但是,如果字符串被加盐(例如,使用字符串“MY_PERSONAL_SALT”),“空字符串”(即“MY_PERSONAL_SALT”)的哈希值将变为aeac2612626724592271634fb14d3ea6,因此回溯不明显。我想说的是,最好使用 any 盐,而不是不使用。因此,了解使用哪种盐并不是很重要。

    实际上有websites that do just this - 你可以给它一个(md5)散列,它会吐出一个已知的明文来生成那个特定的散列。如果您可以访问存储普通 md5 哈希的数据库,那么您只需将管理员的哈希输入此类服务并登录即可。但是,如果密码被加盐,则此类服务将变为无效。

    此外,双重哈希通常被认为是不好的方法,因为它会减少结果空间。所有流行的哈希都是固定长度的。因此,您只能有这个固定长度的有限值,结果变化不大。这个可以算是另一种腌制方式,但我不推荐。

    【讨论】:

    • 目标网站不应该包含任何太敏感的东西(它不是银行),但我还是希望它得到保护。
    • 双重哈希不会减少结果空间。迭代散列是针对字典和蛮力攻击的常见控制(它会减慢它们的速度,而不是减慢您的密码检查速度)。
    • @frankodwyer:是的,这很糟糕。 sha1(sha1($foo)) 有效地减少了输出空间,因为任何内部函数的碰撞都会自动变成外部函数的碰撞。退化是线性的,但它仍然是一个问题。迭代散列方法在每一轮中反馈数据,例如$hash = sha1(sha1($salt . $password) . $salt)。但这仍然不好……坚持使用 PBKDF2 或 Bcrypt……
    【解决方案6】:

    最后,从数学上讲,双重哈希没有任何好处。然而,在实践中,它对于防止基于彩虹表的攻击很有用。换句话说,它并不比使用盐进行散列更有好处,后者在您的应用程序或服务器上花费的处理器时间要少得多。

    【讨论】:

    • 多重哈希还可以防止字典和蛮力攻击 - 即它只是使它们需要更长的计算时间。
    • 双散列不会给您带来显着优势,但多轮散列迭代仍然是抵御字典和布鲁斯力攻击的可行方法。工业强度密码哈希使用 1000 多轮。 PKCS#5 的 PBKDF1 建议至少 1000 发。
    【解决方案7】:

    虽然问题已得到解答,但我只想重申,用于散列的盐应该是随机的,而不是像第一个答案中建议的电子邮件地址。

    更多解释见-http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

    最近我讨论了密码哈希是否用随机盐 比特比可猜测或已知的比特更安全 盐。让我们看看:如果系统存储密码被泄露为 以及存储随机盐的系统,攻击者将 可以访问哈希和盐,所以盐是随机的还是 不,没关系。攻击者可以生成预先计算的 彩虹表来破解哈希。有趣的部分来了——它 生成预先计算的表并不是那么简单。让我们举个例子 WPA 安全模型。您的 WPA 密码实际上从未发送到 无线接入点。相反,它使用您的 SSID( 网络名称 - 如 Linksys、Dlink 等)。一个很好的解释如何 这个作品在这里。为了从哈希中检索密码,您将 需要知道密码以及盐(网络名称)。教堂 Wifi 已经预先计算了具有前 1000 个 SSID 和 大约一百万个密码。所有表的大小约为 40 GB。 正如您在他们的网站上看到的,有人使用了 15 个 FGPA 阵列 3 天 生成这些表。假设受害者使用 SSID 作为 “a387csf3”,密码为“123456”,会被人破解吗 表?不! .. 这不可以。即使密码很弱,表格 没有 SSID a387csf3 的哈希值。这就是拥有之美 随机盐。它将阻止那些依靠预先计算而茁壮成长的饼干 表。它可以阻止一个坚定的黑客吗?可能不是。但是使用 随机盐确实提供了额外的防御层。当我们在 这个话题,让我们讨论存储随机数的额外优势 盐在一个单独的系统上。场景#1:存储密码哈希 在系统 X 上,用于散列的盐值存储在系统 Y 上。 这些盐值是可猜测的或已知的(例如用户名)场景#2: 密码哈希存储在系统 X 上,盐值用于 散列存储在系统 Y 上。这些盐值是随机的。如果 系统 X 已被攻陷,正如你所猜想的那样,有一个巨大的 在单独的系统上使用随机盐的优势(场景 #2)。 攻击者需要猜测附加值才能破解 哈希。如果使用 32 位盐,则 2^32= 4,294,967,296(约 4.2 猜测的每个密码都可能需要十亿)次迭代。

    【讨论】:

    • 即使攻击者获得了盐,“sitesalt:usersalt:password”字符串仍然可以抵抗预先计算的表,因为攻击者需要为每个用户生成表(因此攻击变得慢得多),除非当然是针对特定用户...
    • 关于“即使攻击者得到了盐,“sitesalt:usersalt:password”字符串仍然可以抵抗预计算表”,完全同意。我的观点是,如果将sitesalt设置为随机且长,将使系统比它(sitesalt)可预测的更安全。我看到有人推荐使用电子邮件 ID 等作为盐,我不鼓励这样做。
    • 你错过了我最初写的内容。我说使用随机数,与记录一起存储,加上电子邮件地址。电子邮件地址的添加为黑客的工作提供了额外的熵。从那以后,我改写了我的答案以支持 bcrypt。
    【解决方案8】:

    一个更短更安全的答案 - 根本不要编写自己的密码机制,使用久经考验的机制。

    • PHP 5.5 或更高版本:password_hash() 质量很好,是 PHP 核心的一部分。
    • PHP 4.x(已过时):OpenWall 的 phpass 库比大多数自定义代码要好得多 - 用于 WordPress、Drupal 等。

    大多数程序员只是不具备在不引入漏洞的情况下安全地编写加密相关代码的专业知识。

    快速自测:什么是密码延伸以及您应该使用多少次迭代?如果您不知道答案,您应该使用password_hash(),因为由于更快的 CPU 和使用GPUs and FPGAsbillions of guesses per second 的速率破解密码(使用GPU)。

    截至 2012 年,您可以crack all 8-character Windows passwords in 6 hours 使用安装在 5 台台式电脑中的 25 个 GPU。这是暴力破解,即枚举和检查每个 8 个字符的 Windows 密码,包括特殊字符,而不是字典攻击。借助现代 GPU,您当然可以破解更多密码或使用更少的 GPU,或者以合理的成本在云中租用 GPU 几个小时。

    还有很多针对Windows密码的彩虹表攻击,在普通CPU上运行,速度非常快。

    这一切都是因为 Windows 仍然 doesn't salt or stretch 它的密码 even in Windows 10。 2021 年依然如此。不要犯微软犯过的错误!

    另请参阅:

    • excellent answer 详细了解为什么 password_hash()phpass 是最佳选择。
    • good blog article 为主要算法(包括 bcrypt、scrypt 和 PBKDF2)提供推荐的“工作因子”(迭代次数)。

    【讨论】:

    • 但这些系统更为人所知,可能已经受到攻击。但是当你不知道自己在做什么时,它比自己制作要好。
    • 关于“这些系统更为人所知,并且可能已经受到威胁” - 没有理由仅仅因为其知名度更高而设计良好的身份验证系统就应该成为“已经受到威胁”。 phpass 等库由专家编写,并由许多人详细审查 - 它们众所周知的事实伴随着不同人的详细审查,并且更有可能意味着它们是安全的。
    • “根本不要编写自己的密码机制”——但真正偏执的人会希望自己编写密码机制,以尽量减少 NSA 拥有后门的可能性。
    • @PP - 在我看来,经过同行评审的密码哈希算法具有 NSA 后门的可能性非常低。不是真正的加密专家的人编写没有其他漏洞的新密码哈希机制的机会要低得多。典型的 web 应用程序只使用 MD5 或 SHA-1 哈希,这很糟糕 - 甚至 Chris Shiflett 的其他很棒的 Essential PHP Security 书也推荐 MD5 ...
    • phpass 不是最好的方法。从来没有,而且很可能永远不会。几年前我查看了代码,它在 Windows 或 /dev/urandom 不可用的任何平台上都不安全。在安全性方面,它不遵循最佳实践,在应该终止应用程序而不是对安全性做出虚假声明时使用 md5() 和 microtime() 的组合。尽管 PHP 本身在安全领域以 bcrypt 为核心,但自从我查看代码以来,它还没有看到任何更新。远离 phpass。
    【解决方案9】:

    我正在使用Phpass,这是一个简单的单文件 PHP 类,几乎可以在每个 PHP 项目中轻松实现。另见The H

    默认情况下,它使用在 Phpass 中实现的最强大的可用加密,即 bcrypt,并回退到 MD5 的其他加密,以提供对 Wordpress 等框架的向后兼容性。

    返回的哈希可以按原样存储在数据库中。生成哈希的示例用法是:

    $t_hasher = new PasswordHash(8, FALSE);
    $hash = $t_hasher->HashPassword($password);
    

    要验证密码,可以使用:

    $t_hasher = new PasswordHash(8, FALSE);
    $check = $t_hasher->CheckPassword($password, $hash);
    

    【讨论】:

      【解决方案10】:

      我只想指出,PHP 5.5 包含一个password hashing API,它提供了一个围绕crypt() 的包装器。此 API 显着简化了散列、验证和重新散列密码散列的任务。作者还发布了一个compatibility pack(以单个password.php 文件的形式,您只需require 即可使用),供那些使用PHP 5.3.7 及更高版本并想立即使用它的人使用。

      它目前仅支持 BCRYPT,但它旨在轻松扩展以包含其他密码哈希技术,并且由于该技术和成本作为哈希的一部分存储,因此更改您首选的哈希技术/成本不会使当前哈希无效,框架将自动在验证时使用正确的技术/成本。如果您没有明确定义自己的盐,它还会处理生成“安全”盐。

      API 公开了四个函数:

      • password_get_info() - 返回有关给定哈希的信息
      • password_hash() - 创建密码哈希
      • password_needs_rehash() - 检查给定哈希是否与给定选项匹配。有助于检查散列是否符合您当前的技术/成本方案,允许您在必要时重新散列
      • password_verify() - 验证密码是否与哈希匹配

      目前,这些函数接受 PASSWORD_BCRYPT 和 PASSWORD_DEFAULT 密码常量,它们目前是同义词,不同之处在于 PASSWORD_DEFAULT“可能会在更新的 PHP 版本中发生变化,因为它支持更新、更强大的哈希算法。”在登录时使用 PASSWORD_DEFAULT 和 password_needs_rehash()(并在必要时重新散列)应确保您的散列对蛮力攻击具有相当的弹性,对您几乎没有任何工作。

      编辑:我刚刚意识到罗伯特 K 的回答中简要提到了这一点。我将把这个答案留在这里,因为我认为它提供了更多关于它的工作原理以及它为那些不了解安全性的人提供的易用性的信息。

      【讨论】:

        【解决方案11】:

        我在这里找到了关于这个问题的完美话题:https://crackstation.net/hashing-security.htm,我希望你从中受益,这里还有源代码,也可以防止基于时间的攻击。

        <?php
        /*
         * Password hashing with PBKDF2.
         * Author: havoc AT defuse.ca
         * www: https://defuse.ca/php-pbkdf2.htm
         */
        
        // These constants may be changed without breaking existing hashes.
        define("PBKDF2_HASH_ALGORITHM", "sha256");
        define("PBKDF2_ITERATIONS", 1000);
        define("PBKDF2_SALT_BYTES", 24);
        define("PBKDF2_HASH_BYTES", 24);
        
        define("HASH_SECTIONS", 4);
        define("HASH_ALGORITHM_INDEX", 0);
        define("HASH_ITERATION_INDEX", 1);
        define("HASH_SALT_INDEX", 2);
        define("HASH_PBKDF2_INDEX", 3);
        
        function create_hash($password)
        {
            // format: algorithm:iterations:salt:hash
            $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
            return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
                base64_encode(pbkdf2(
                    PBKDF2_HASH_ALGORITHM,
                    $password,
                    $salt,
                    PBKDF2_ITERATIONS,
                    PBKDF2_HASH_BYTES,
                    true
                ));
        }
        
        function validate_password($password, $good_hash)
        {
            $params = explode(":", $good_hash);
            if(count($params) < HASH_SECTIONS)
               return false; 
            $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
            return slow_equals(
                $pbkdf2,
                pbkdf2(
                    $params[HASH_ALGORITHM_INDEX],
                    $password,
                    $params[HASH_SALT_INDEX],
                    (int)$params[HASH_ITERATION_INDEX],
                    strlen($pbkdf2),
                    true
                )
            );
        }
        
        // Compares two strings $a and $b in length-constant time.
        function slow_equals($a, $b)
        {
            $diff = strlen($a) ^ strlen($b);
            for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
            {
                $diff |= ord($a[$i]) ^ ord($b[$i]);
            }
            return $diff === 0; 
        }
        
        /*
         * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
         * $algorithm - The hash algorithm to use. Recommended: SHA256
         * $password - The password.
         * $salt - A salt that is unique to the password.
         * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
         * $key_length - The length of the derived key in bytes.
         * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
         * Returns: A $key_length-byte key derived from the password and salt.
         *
         * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
         *
         * This implementation of PBKDF2 was originally created by https://defuse.ca
         * With improvements by http://www.variations-of-shadow.com
         */
        function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
        {
            $algorithm = strtolower($algorithm);
            if(!in_array($algorithm, hash_algos(), true))
                die('PBKDF2 ERROR: Invalid hash algorithm.');
            if($count <= 0 || $key_length <= 0)
                die('PBKDF2 ERROR: Invalid parameters.');
        
            $hash_length = strlen(hash($algorithm, "", true));
            $block_count = ceil($key_length / $hash_length);
        
            $output = "";
            for($i = 1; $i <= $block_count; $i++) {
                // $i encoded as 4 bytes, big endian.
                $last = $salt . pack("N", $i);
                // first iteration
                $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
                // perform the other $count - 1 iterations
                for ($j = 1; $j < $count; $j++) {
                    $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
                }
                $output .= $xorsum;
            }
        
            if($raw_output)
                return substr($output, 0, $key_length);
            else
                return bin2hex(substr($output, 0, $key_length));
        }
        ?>
        

        【讨论】:

        • 你给我们的解决方案没有使用没有使用
        【解决方案12】:

        要记住的事情

        关于 PHP 的密码加密已经说了很多,其中大部分都是非常好的建议,但在您开始使用 PHP 进行密码加密的过程之前,请确保您已实施或准备实施以下内容。

        服务器

        港口

        如果您没有正确保护运行 PHP 和 DB 的服务器,那么无论您的加密有多好,您的所有努力都是毫无价值的。大多数服务器的功能相对相同,它们分配了端口以允许您通过 ftp 或 shell 远程访问它们。确保更改您激活的远程连接的默认端口。通过不这样做,您实际上使攻击者在访问您的系统时少了一步。

        用户名

        不要使用用户名 admin、root 或类似名称。此外,如果您使用的是基于 unix 的系统,请不要让 root 帐户登录可访问,它应该始终只能是 sudo。

        密码

        您告诉您的用户使用正确的密码以避免被黑客入侵,您也要这样做。当后门大开时,努力锁上前门有什么意义。

        数据库

        服务器

        理想情况下,您希望您的数据库和应用程序位于不同的服务器上。由于成本原因,这并不总是可行的,但它确实提供了一定的安全性,因为攻击者必须经过两个步骤才能完全访问系统。

        用户

        始终让您的应用程序拥有自己的帐户来访问数据库,并且只授予它所需的权限。

        然后为您创建一个单独的用户帐户,该帐户不会存储在服务器的任何位置,甚至不会存储在应用程序中。

        像往常一样,不要做这个根或类似的东西。

        密码

        遵循与所有良好密码相同的准则。也不要在同一系统上的任何 SERVER 或 DB 帐户上重复使用相同的密码。

        PHP

        密码

        永远不要在你的数据库中存储密码,而是存储哈希和唯一的盐,我稍后会解释原因。

        散列

        单向哈希!!!!!!!,永远不要以可以反转的方式对密码进行哈希,哈希应该是一种方式,这意味着您不会将它们反转并将它们与密码进行比较,而是哈希以相同的方式输入密码并比较两个哈希值。这意味着即使攻击者可以访问数据库,他也不知道实际密码是什么,只知道其生成的哈希值。这意味着在最坏的情况下为您的用户提供更高的安全性。

        那里有很多很好的散列函数(password_hashhash 等...),但您需要选择一个好的算法才能使散列有效。 (bcrypt 和类似的算法都是不错的算法。)

        当哈希速度是关键时,越慢越能抵抗蛮力攻击。

        散列中最常见的错误之一是散列不是用户独有的。这主要是因为盐不是唯一生成的。

        腌制

        密码在散列前应始终加盐。 Salting 会在密码中添加一个随机字符串,因此类似的密码在数据库中不会出现相同的情况。但是,如果盐不是每个用户唯一的(即:您使用硬编码的盐),那么您几乎会使您的盐一文不值。因为一旦攻击者找出一个密码盐,他就拥有了所有这些盐。

        当您创建盐时,请确保它对于盐的密码是唯一的,然后将完成的哈希和盐存储在您的数据库中。这样做的目的是使攻击者必须单独破解每个盐和哈希,然后才能获得访问权限。这意味着攻击者需要更多的工作和时间。

        用户创建密码

        如果用户通过前端创建密码,则意味着必须将其发送到服务器。这带来了一个安全问题,因为这意味着未加密的密码被发送到服务器,如果攻击者能够监听和访问,那么您在 PHP 中的所有安全性都是毫无价值的。始终安全地传输数据,这是通过 SSL 完成的,但是即使 SSL 也不是完美无缺的(OpenSSL 的 Heartbleed 缺陷就是一个例子)。

        还要让用户创建一个安全的密码,这很简单,应该一直这样做,用户最终会感激不尽的。

        最后,无论您采取什么安全措施都是 100% 安全的,要保护的技术越先进,攻击就越先进。但遵循这些步骤将使您的网站更加安全,并且更不适合攻击者追捕。

        这是一个 PHP 类,可以轻松地为密码创建哈希和盐

        http://git.io/mSJqpw

        【讨论】:

        • 你应该从你的哈希算法列表中删除 SHA512,因为它太快了。仅与 PBKDF2 结合使用。虽然 BCrypt 基于河豚,但河豚本身是一种加密算法,而不是散列算法。
        • 如何将随机盐存储在数据库中?我认为您不会散列它(不能用于验证),也不会明文存储(如果攻击者可以读取数据库,则没有真正的好处)。那么,你是怎么做到的呢?
        • wmfrancia 写道:“Salting 会在密码中添加一个随机字符串,因此类似的密码在数据库中不会出现相同的情况”。这对我来说没有意义。数据库中的散列已经看起来不同了,因为这是散列函数的一个属性。
        • wmfancia 写到关于恒定盐:“一旦攻击者找出一个密码盐,他就拥有了所有盐的盐”。同样可以说,如果黑客找出了哪个数据库字段是盐,那么他就拥有了所有这些盐。由于恒定盐可能不会在数据库中,因此恒定盐是一件好事。
        • 当然,这些 cmets 并不是建议每个用户随机使用一个盐并不比每个应用程序一个盐好。更好。
        【解决方案13】:

        从 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() 会使用它。

        【讨论】:

          【解决方案14】:

          好的 有时我们需要盐 盐必须是唯一的 所以让我们生成它

             /**
               * Generating string
               * @param $size
               * @return string
               */
              function Uniwur_string($size){
                  $text = md5(uniqid(rand(), TRUE));
                  RETURN substr($text, 0, $size);
              }
          

          我们还需要哈希 我正在使用 sha512 这是最好的,它在 php 中

             /**
               * Hashing string
               * @param $string
               * @return string
               */
              function hash($string){
                  return hash('sha512', $string);
              }
          

          所以现在我们可以使用这个函数来生成安全密码了

          // generating unique password
          $password = Uniwur_string(20); // or you can add manual password
          // generating 32 character salt
          $salt = Uniwur_string(32);
          // now we can manipulate this informations
          
          // hashin salt for safe
          $hash_salt = hash($salt);
          // hashing password
          $hash_psw = hash($password.$hash_salt);
          

          现在我们需要在数据库中保存我们的 $hash_psw 变量值和 $salt 变量

          为了授权,我们将使用相同的步骤...

          这是保护我们客户密码的最佳方式...

          附:对于最后两个步骤,您可以使用自己的算法... 但请确保您将来可以生成此哈希密码 当您需要授权用户时...

          【讨论】:

          • 这个问题是关于密码哈希的。 1 次执行sha512(即使加盐)被广泛认为不足以保护密码。 (另外,RNG 不是加密安全的,因此使用它来生成密码是有风险的)。
          • 你不知道自己在做什么。阅读这篇文章中的热门答案,您就会明白为什么您的代码不仅不安全,而且毫无意义。
          • 好的。我的代码不安全。所以让我知道你为什么在你的算法中只使用 sha256 ???我知道 sha512 是最好的 为什么不用呢???
          • @shalvasoft sha512 非常适合通用散列,但密码保护需要具有非常特定属性的散列(例如,“变慢”是一件奇怪的好事,而且sha512 相当快)。有人使用 sha512 作为构建块来创建密码哈希函数,但现在推荐的方法是“使用 bcrypt 并密切关注 scrypt”。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-06-09
          相关资源
          最近更新 更多