保护PHP密码的哈希和盐值


1174

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

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

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

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

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

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

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

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


13
openwall.com/phpass还是一个很好的库
Alfred

51
Md5

3
@NSAwesomeGuy这取决于您使用它的目的。当然,对于彩虹匹配或仅是蛮力地使用未加盐的MD5密码来说,这是微不足道的,但是通过适当的加盐处理,为快速破解多组密码而建立彩虹表仍然是极其不切实际的,蛮力是绝无仅有的。
克雷格·林格


Answers:


982

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

从那时起,PHP就给了我们password_hashpassword_verify并且自介绍以来,它们是推荐的密码哈希和检查方法。

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

TL; DR

不要

  • 不要限制用户可以输入哪些字符作为密码。只有白痴这样做。
  • 不要限制密码的长度。如果您的用户想要一个句子,其中包含超级校准,容易理解的句子,请不要阻止他们使用它。
  • 不要在密码中剥离或转义HTML和特殊字符。
  • 切勿以纯文本形式存储用户密码。
  • 除非用户忘记了密码,否则不要通过电子邮件将密码发送给您的用户,而是您发送了一个临时密码
  • 永远不要以任何方式记录密码。
  • 切勿使用SHA1或MD5甚至SHA256 哈希密码!现代饼干可以分别超过60和1800亿个哈希/秒。
  • 不要将bcrypt与hash()的原始输出混合使用,请使用十六进制输出或base64_encode对其进行编码。(这适用于其中可能有恶意的任何输入\0,这会严重削弱安全性。)

多斯

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

为什么仍要哈希密码?

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

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

Whitehat Security的首席技术官Jeremiah Grossman 在最近的密码恢复后要求在蛮力破解他的密码保护之后,在White Hat Security博客上说。

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

(强调我的。)

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

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

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

密码熵近似容易。使用整个范围的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函数的文档,如果您想了解如何使用bcrypt,或者为自己找到一个好的 包装器,或者将类似PHPASS的东西用于更传统的实现。我建议至少进行12轮bcrypt,如果不是15到18轮。

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

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

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


9
机密信息无济于事,因为您的密码数据库无论如何都应该是机密的-如果他们可以控制该数据库,他们也可以找到您正在使用的机密信息。然而,重要的是盐是无规的。
frankodwyer

2
注意,“解密的计算能力”还不存在,这并不是真的。由于大多数密码是字典单词或字典派生的,因此基于字典的攻击通常非常有效(因此使用了密码策略和迭代计数)。
frankodwyer

6
@邪恶的跳蚤,我没有和你吵架。只是指出我们这一领域的工作是多么复杂和复杂。我一直希望能从建立小型网站的内容管理系统的最根本,最聪明的最佳实践中受益。我还在这里学习。...每当我阅读有意义的内容时,我很快就会发现其他5篇与之矛盾的文章。一圈又一圈迅速晕眩:)
m42

4
有趣的修订。用户ID(例如,自动递增BIGINT)是否是一个好的随机数?还是因为它不是随机的,所以不好吗?此外,我还必须将每个用户的随机数存储在数据库中...站点密钥+随机数+ HMAC是否比经过多次迭代的盐腌(带有用户ID)哈希显着提高了安全性?同样,多次迭代HMAC是否对安全性有利?
luiscubal

4
通过电子邮件发送需要用户在首次使用时进行更改的临时密码,以及通过电子邮件发送允许其设置密码的“安全”链接同样具有风险。在任何一种情况下,任何拦截电子邮件的人都可以访问该帐户,只要他们在预期的收件人之前使用链接或密码即可。
蒂姆·高铁

138

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

  • 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 仍然 不会加盐或扩展其密码-不要犯与Microsoft相同的错误!

也可以看看:

  • 出色的答案,以及更多关于为什么password_hash()或是phpass最佳方法的信息。
  • 一篇不错的博客文章,为bcrypt,scrypt和PBKDF2等主要算法提供了建议的“工作因子”(迭代次数)。

1
但是这些系统更广为人知,也许已经遭到破坏。但是当您不知道自己在做什么时,胜过自己做。
JqueryToAddNumbers '02

14
关于“这些系统更广为人知,并且可能已经被破坏”-没有理由仅仅因为众所周知的良好设计的身份验证系统就应该“已经受到破坏”。诸如phpass之类的库是由专家编写的,并且受到了许多人的详细审查-众所周知的事实是,不同的人也进行了详细的审查,这很可能意味着它们是安全的。
RichVel 2012年

鉴于最近来自LinkedIn,Last.fm和其他网站的密码哈希转储,这已经成为话题。您不知道如何编写自己的密码机制,这是您的好伙伴!
RichVel 2012年

3
@PP-在我看来,具有NSA后门的经过同行评审的密码哈希算法的机会非常低。不是真正的加密专家的人编写没有其他漏洞的新密码哈希机制的机会要低得多。而且典型的Web应用程序仅使用MD5或SHA-1哈希,这很糟糕-甚至Chris Shiflett的原本很棒的Essential PHP Security书籍也推荐MD5 ...
RichVel 2013年

1
@RichVel-password_hash()函数。如前所述,它内置在PHP核心(aka / ext / standard)中。
CubicleSoft 2015年

43

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


不用于密码哈希。攻击者只需破坏一次哈希即可检索密码。无论如何,这很重要,因为在密码方案中,MD5和SHA1都没有任何实际可用的中断。
frankodwyer

2
抱歉,我误读了您的建议使用两个哈希值的答案...您实际上是正确的。在密码情况下,使用两个散列会削弱系统,因为它们只需要破坏较弱的散列即可。
frankodwyer

40

从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()从中提取数据,因此旧哈希将继续进行验证。


33

尽管已经回答了该问题,但我只想重申,用于哈希处理的盐应该是随机的,而不是像第一个答案中所建议的电子邮件地址一样。

有关更多说明,请参见-http : //www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

最近,我讨论了用随机位加盐的密码散列是否比用可猜测或已知盐加盐的密码散列更安全。让我们看看:如果存储密码的系统以及存储随机盐的系统遭到破坏,则攻击者将可以访问哈希和盐,因此盐是否是随机的都没有关系。攻击者可以生成预先计算的彩虹表来破解哈希。有趣的部分来了-生成预先计算的表并不是那么简单。让我们以WPA安全模型为例。实际上,您的WPA密码永远不会发送到Wireless Access Point。而是使用您的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(约42亿)次迭代。密码哈希存储在系统X上,用于哈希的盐值存储在系统Y上。这些盐值是可猜测的或已知的(例如,用户名)方案2:密码哈希存储在系统X上,用于哈希的盐值存储在系统X上系统Y。这些盐值是随机的。如您所料,如果系统X受到破坏,则在单独的系统上使用随机盐具有巨大优势(方案2)。攻击者将需要猜测附加值才能破解哈希。如果使用32位盐,则每个猜测的密码将需要2 ^ 32 = 4,294,967,296(约42亿)次迭代。密码哈希存储在系统X上,用于哈希的盐值存储在系统Y上。这些盐值是可猜测的或已知的(例如,用户名)方案2:密码哈希存储在系统X上,用于哈希的盐值存储在系统X上系统Y。这些盐值是随机的。如您所料,如果系统X受到破坏,则在单独的系统上使用随机盐具有巨大优势(方案2)。攻击者将需要猜测附加值才能破解哈希。如果使用32位盐,则每个猜测的密码将需要2 ^ 32 = 4,294,967,296(约42亿)次迭代。这些盐值是随机的。如您所料,如果系统X受到破坏,则在单独的系统上使用随机盐具有巨大优势(方案2)。攻击者将需要猜测附加值才能破解哈希。如果使用32位盐,则每个猜测的密码将需要2 ^ 32 = 4,294,967,296(约42亿)次迭代。这些盐值是随机的。如您所料,如果系统X受到破坏,则在单独的系统上使用随机盐具有巨大优势(方案2)。攻击者将需要猜测附加值才能破解哈希。如果使用32位盐,则每个猜测的密码将需要2 ^ 32 = 4,294,967,296(约42亿)次迭代。


7
即使攻击者加了盐,“ sitesalt:usersalt:password”字符串仍然可以抵抗预先计算的表,因为攻击者需要为每个用户生成表(因此,攻击会变慢得多),除非特定的用户是有针对性的...
luiscubal 2010年

关于“即使攻击者加了盐,“ sitesalt:usersalt:password”字符串仍然可以抵抗预先计算的表”,完全同意。我的观点是,sitesalt如果随机且较长,则将使系统比可预测的(sitesalt)更安全。我见过有人建议使用电子邮件ID等作为盐,我不鼓励这样做。
Gaurav Kumar '02

你错过了我最初写的东西。我说过使用随机随机数,与记录一起存储,再加上电子邮件地址。电子邮件地址的添加使黑客可以继续工作。从那以后,我改写了我的答案,转而支持bcrypt。
罗伯特·K

28

我只想指出,PHP 5.5包含一个密码哈希API,该API提供了包装crypt()。该API大大简化了哈希,验证和重新哈希密码哈希的任务。作者还发布了一个兼容性包(以您仅需require使用的单个password.php文件的形式),适用于使用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的答案中简短提到的。我将在这里留下这个答案,因为我认为它为不了解安全性的人提供了更多有关其工作方式和易用性的信息。


19

我正在使用Phpass,这是一个简单的单文件PHP类,几乎可以在每个PHP项目中轻松实现。另请参见第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);

14

谨记

关于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


1
您应该从体面的哈希算法列表中删除SHA512,因为它太快了。仅与PBKDF2结合使用。尽管BCrypt基于河豚,但河豚本身是用于加密而非散列的算法。
martinstoeckli 2014年

1
您如何将随机盐存储在数据库中?我认为您不会对它进行哈希处理(不能用于验证),也不会对其进行明确存储(如果攻击者可以读取数据库,则没有任何实际好处)。那么,你如何做呢?
Iazel 2014年

wmfrancia写道:“ Salting为密码添加了一个随机字符串,因此相似的密码在数据库中不会显示相同的密码”。这对我来说没有意义。数据库中的散列已经看起来不一样,因为这是散列函数的属性。
H2ONaCl

wmfancia谈到了一种恒盐:“一旦攻击者找出一个密码盐,他就会为所有盐都加盐”。可以说,如果黑客弄清楚哪个DB字段是盐,则他拥有所有盐。由于恒盐可能不在数据库中,因此对恒盐而言这是一件好事。
H2ONaCl

当然,这些评论并不是要建议每个用户随机添加一个盐并不比每个应用程序添加一个盐更好。这个比较好。
H2ONaCl

12

Google说SHA256可用于PHP。

您绝对应该使用盐。我建议使用随机字节(不要将自己局限于字符和数字)。通常,您选择的时间越长,它越安全,越慢。我想64字节应该没问题。


13
64位对任何人都应该足够?
Konerak 2011年

@ Konerak,20年后,我会回到这个话题。:)但是确实可以使用SHA256。如果你想知道如何SHA256是安全的,你可能要检查了这一点:security.stackexchange.com/questions/90064/...
文森特·爱德华Gedaria Binua

8

最后,从数学上讲,双重哈希没有任何好处。但是,实际上,它对于防止基于彩虹表的攻击很有用。换句话说,它没有比加盐散列更多的好处,盐散列可以减少应用程序或服务器上的处理器时间。


2
多重哈希还可以防止字典和蛮力攻击-即,仅使它们花费更长的时间进行计算。
frankodwyer

6
双重哈希不会给您带来明显的优势,但是多轮哈希迭代仍然是抵御字典和布鲁斯攻击的可行方法。工业强度密码散列使用1000余回合。PKCS#5的PBKDF1建议最少发1000发子弹。
Berk D. Demir

8

我在这里找到了关于此问题的完美话题: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));
}
?>

您给我们的解决方案没有任何使用就没有使用
迈克尔

6

我通常将SHA1和salt与用户ID(或其他一些特定于用户的信息)一起使用,有时我还额外使用常量salt(因此,盐有2部分)。

现在也认为SHA1受到了一定影响,但程度要比MD5小得多。通过使用一种盐(任何盐),您可以防止使用通用的彩虹表来攻击您的哈希(有些人甚至通过搜索哈希将Google作为一种彩虹表成功使用)。可以想象,攻击者可能会使用您的盐生成彩虹表,因此这就是为什么您应该包含用户特定的盐的原因。这样,他们将不得不为系统中的每条记录生成一个彩虹表,而不仅仅是整个系统中的一个!使用这种类型的盐腌,即使MD5也相当安全。


2
恒盐不是一个好主意……可能不是致命的缺陷,但不必要地削弱了方案。
frankodwyer

MD5和SHA1速度很快,因此这是一个糟糕的死命。
CodesInChaos 2014年

4

在可预见的将来,SHA1和一种盐就足够了(自然取决于您是为Fort Knox编码还是为购物清单输入登录系统)。如果SHA1对您来说不够好,请使用SHA256

可以这么说,加盐的想法是使散列结果失去平衡。例如,已知一个空字符串的MD5哈希为d41d8cd98f00b204e9800998ecf8427e。因此,如果内存足够好的人会看到该哈希,并且知道它是一个空字符串的哈希。但是,如果将字符串加盐(例如,使用字符串“ MY_PERSONAL_SALT”),则“空字符串”(即“ MY_PERSONAL_SALT”)的哈希将变为aeac2612626724592271634fb14d3ea6,因此回溯不明显。我要说的是,使用任何盐比不使用盐更好。因此,知道使用哪种盐并不是太重要。

实际上有一些网站可以做到这一点 -您可以为它提供(md5)哈希,它会吐出一个生成该哈希的已知明文。如果您可以访问存储纯md5-哈希的数据库,那么对于管理员来说,输入此类服务的哈希值并登录就不那么容易了。但是,如果密码过分,此类服务就会变成无效的。

同样,双重哈希通常被认为是不好的方法,因为它会减少结果空间。所有流行的哈希都是固定长度的。因此,您只能具有此固定长度的有限值,结果变化也较少。这可以被视为另一种盐腌形式,但我不建议这样做。


目标站点不应包含任何过于敏感的内容(它不是银行),但我还是希望它具有安全性。
luiscubal

1
双重哈希不会减少结果空间。迭代散列是针对字典和蛮力攻击的一种常见控制方法(它使它们变慢的速度远远大于它使密码检查变慢的速度)。
frankodwyer

2
@frankodwyer:是的,这很糟糕。sha1(sha1($foo))有效减少输出空间,因为内部函数的任何冲突都会自动成为外部函数的冲突。降级是线性的,但仍然值得关注。迭代哈希方法会在每一轮反馈数据,例如$hash = sha1(sha1($salt . $password) . $salt)。但这仍然不好...坚持使用PBKDF2或Bcrypt ...
ircmaxell 2012年

-7

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

   /**
     * 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变量保存在数据库中

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

这是保护客户密码的最佳方法...

请注意最后两步,您可以使用自己的算法...但是请确保将来需要授权用户时可以生成此哈希密码...


4
这个问题是关于密码的哈希。sha512普遍认为1执行(即使已加盐)不足以进行密码保护。(同样,RNG也不是加密安全的,因此将其用于密码生成是有风险的)。
luiscubal 2015年

2
你不知道自己在做什么。阅读本文中最重要的答案,您会发现为什么您的代码不仅不安全,而且毫无意义。

好。我的代码不安全。所以让我知道你为什么在sha256上使用算法???我知道sha512是最好的为什么不使用它???
shalvasoft

1
@shalvasoft sha512对于通用哈希来说非常不错,但是密码保护需要具有非常特定属性的哈希(例如,“变慢”是一件好事,例如,sha512相当快)。有些人已经使用sha512作为创建密码哈希函数的构建块,但是如今推荐的方法是“使用bcrypt并注意scrypt”。
luiscubal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.