保护PHP密码的哈希和盐值 - php

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

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

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

哈希机制必须在PHP中可用
必须安全
它可以使用盐(在这种情况下,所有盐都一样好吗?是否有任何方法可以生成优质盐?)

另外,我是否应该在数据库中存储两个字段(例如,一个使用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

参考方案

免责声明:此答案写于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的任何输入,这会严重削弱安全性。)

多斯

尽可能使用scrypt;如果不能,请使用bcrypt。
如果不能使用带有SHA2散列的bcrypt或scrypt,请使用PBKDF2。
数据库受到威胁时,请重置每个人的密码。
实施合理的8-10个字符的最小长度,并要求至少1个大写字母,1个小写字母,一个数字和一个符号。这将改善密码的熵,从而使其更难以破解。 (有关一些辩论,请参见“什么才是好的密码?”部分。)

为什么仍要哈希密码?

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

您想要对用户帐户进行良好而健壮的哈希处理的另一个原因是,给您足够的时间来更改系统中的所有密码。如果您的数据库遭到破坏,您将需要足够的时间至少锁定系统,如果不更改数据库中的每个密码。

Whitehat Security的首席技术官Jeremiah Grossman在最近的密码恢复中要求强行破坏其密码保护后,stated on White Hat Security blog:

有趣的是,在摆脱这场噩梦的过程中,我学到了很多我不知道的有关密码破解,存储和复杂性的信息。我已经开始欣赏为什么密码存储比密码复杂性重要得多了。如果您不知道密码的存储方式,那么您真正需要依靠的就是复杂性。这可能是密码和加密专家的常识,但是对于普通的InfoSec或Web安全专家而言,我对此表示高度怀疑。

(强调我的。)

无论如何,什么才是好的密码?

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

简而言之,熵就是密码内的变化量。当密码仅是小写罗马字母时,则只有26个字符。差异不大。字母数字密码最好使用36个字符。但是允许带符号的大写和小写字母大约是96个字符。这比仅字母要好得多。一个问题是,为了使密码易于记忆,我们插入了模式-从而减少了熵。糟糕!

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

Crypto StackExchange站点上关于密码熵的讨论更长。优质的Google搜索也会带来很多结果。

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

据我所知,使世界上最好的密码是Catch-22。它不是令人难忘,太可预测,太短,太多的Unicode字符(在Windows /移动设备上很难键入),太长的时间等等。没有密码能真正满足我们的目的,因此我们必须像保护它们一样保护它们在诺克斯堡。

最佳实践

Bcrypt和scrypt是当前的最佳实践。 Scrypt在时间上会比bcrypt更好,但是它尚未被Linux / Unix或Web服务器视为采用标准,并且尚未对其算法进行深入审查。但是,该算法的未来看起来确实很有希望。如果您使用的是Ruby,则有一个scrypt gem可以帮助您,Node.js现在拥有自己的scrypt包。您可以通过Scrypt扩展名或Libsodium扩展名(在PECL中都可用)在PHP中使用Scrypt。

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

当我得知bcrypt仅使用河豚鱼的密钥计划以及可变成本机制时,我改变了主意。后者使您可以通过增加河豚的本已昂贵的密钥计划来增加暴力破解密码的成本。

一般做法

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

但是,假设您根本无法使用bcrypt或PHPASS。然后怎样呢?

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

未来实践

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

密码学回顾与免责声明

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

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

最后:我不是密码学家。我所说的只是我的看法,但我碰巧认为这是基于良好的常识……以及大量的阅读资料。请记住,要尽可能地偏执,使事情难以侵入,然后,如果您仍然担心,请联系白帽黑客或密码专家,以了解他们对您的代码/系统的看法。

PHP-将日期插入日期时间字段 - php

我已在数据库中使用datetime字段存储日期,使用PHP将“今天的日期”插入该字段的正确方法是什么?干杯, 参考方案 我认为您可以使用php date()函数

PHP getallheaders替代 - php

我正在尝试从服务器上的apache切换到nginx。唯一的问题是我在PHP脚本中使用的getallheaders()函数,该函数不适用于Nginx。我已经尝试过用户在getallheaders函数上的php站点上提供的注释,但这并不返回所有请求标头。请告诉我如何解决这个问题。我真的想切换到Nginx。 参考方案 您仍然可以使用它,但是您必须像这里一样重新定义…

PHP mysqli获取查询返回的第一行的值 - php

我正在使用mysqli从数据库中获取某些数据。我正在使用的查询已设置为仅从数据库返回一行。有没有一种方法可以在不使用while循环的情况下获取该行的值?我知道一个while循环对于返回多于一行的行很有用,但是如果不需要while循环,我想避免这种情况,因为不必要的代码是不好的编程。 参考方案 是的-您可以使用:$row = $result->fetch…

php Singleton类实例将在多个会话中保留吗? - php

举一个简单的例子,如果我想计算一个不使用磁盘存储的脚本的命中次数,我可以使用静态类成员来执行此操作吗?用户1:<?php $test = Example::singleton(); $test->visits++; ?> 用户2:<?php $test = Example::singleton(); $test->visits+…

更改默认的URL PHP - php

如何更改默认网址。例如www.example.com/index.php-> www.example.com现在,我要将其设置为www.example.com/test.php。我应该在php.ini中进行更改吗? 参考方案 假设您正在使用apache,则可以通过DirectoryIndex指令执行此操作。Check out the docs。