如何用长嘴鳕来散列长密码(> 72个字符)
上周我读了很多关于密码散列的文章,Blowfish似乎是(现在)最好的散列算法之一 - 但这不是这个问题的主题!
72个字符限制
河豚只考虑输入密码中的前72个字符:
<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);
$input = substr($password, 0, 72);
var_dump($input);
var_dump(password_verify($input, $hash));
?>
输出是:
string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)
正如你所看到的,只有前72个字符很重要。 Twitter正在使用blowfish aka bcrypt来存储他们的密码(https://shouldichangemypassword.com/twitter-hacked.php),并猜测:将您的Twitter密码更改为超过72个字符的长密码,并且您可以通过登录到您的帐户只输入前72个字符。
河豚和胡椒
对“密码”密码有很多不同的意见。 有些人说这是不必要的,因为你必须假定秘密胡椒串也是已知的/已发布的,所以它不会增强哈希。 我有一个单独的数据库服务器,所以很可能只有数据库被泄露,而不是胡椒。
在这种情况下(胡椒不会泄漏),您基于字典进行攻击更加困难(如果这不正确,请纠正我)。 如果你的胡椒串也泄漏了:没有那么糟糕 - 你仍然有盐,并且它没有胡椒一样好受保护。
所以我认为密码至少是不错的选择。
建议
我的建议是为超过72个字符(和胡椒)的密码获取Blowfish哈希值:
<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";
// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);
// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);
var_dump(password_verify($input_peppered, $hash));
?>
这是基于这个问题: password_verify
返回false
。
问题
什么是更安全的方式? 首先获取SHA-256哈希(返回64个字符)或仅考虑密码的前72个字符?
优点
缺点
编辑1:这个问题只针对blowfish / bcrypt的PHP集成。 感谢您的评论!
这里的问题基本上是一个熵的问题。 那么让我们开始看看那里:
每个字符的熵
每个字节的熵的位数是:
那么,我们的行为取决于我们期望的角色类型。
第一个问题
您的代码的第一个问题是,您的“胡椒”散列步骤正在输出十六进制字符(因为没有设置hash_hmac()
的第四个参数)。
因此,通过对胡椒进行哈希处理,可以有效地将密码的最大熵减少2倍(从576到288位)。
第二个问题
然而, sha256
只提供了256
位的熵。 所以你可以有效地将可能的576位降低到256位。 你的哈希步骤*立即*,通过非常定义,至少失去密码中可能熵的50%。
你可以通过切换到SHA512
来部分解决这个问题,你只能将可用熵减少12%左右。 但这仍然是一个不小的差异。 这12%将排列的数量减少了1.8e19
。 这是一个很大的数字...而这就是它通过减少...的因素
相关问题
根本问题是有超过72个字符的密码有三种类型。 这种风格系统对他们的影响将会非常不同:
注意:从现在开始,我假设我们正在比较一个使用SHA512
和原始输出(不是十六进制)的胡椒系统。
高熵随机密码
这些是您的用户使用密码生成器,这些生成器会生成大量密码的密码。 它们是随机的(生成的,不是人为选择的),并且每个字符具有高熵。 这些类型使用高字节(字符> 127)和一些控制字符。
对于这个组,你的哈希函数将大大减少它们的可用熵到bcrypt
。
让我再说一遍。 对于使用高熵长密码的用户,您的解决方案会显着降低密码强度,并且数量可观。 (对于72个字符的密码,熵丢失了62位,而对于更长的密码则更多)
中等熵随机密码
该组使用包含通用符号的密码,但没有高字节或控制字符。 这些是你的可打字密码。
对于这个组,你将稍微解开更多的熵(不创建它,但允许更多的熵适合bcrypt密码)。 当我稍微说一点,我的意思是略微。 当您最大限度地利用SHA512所具有的512位时,就会发生收支平衡。 因此,峰值是78个字符。
让我再说一遍。 对于这类密码,您只能在用完熵之前再存储6个字符。
低熵非随机密码
这是使用可能不会随机生成的字母数字字符的组。 像圣经报价或类似的东西。 这些短语每个字符有大约2.3比特的熵。
对于这个组,你可以通过散列来显着解锁更多的熵(不是创建它,但允许更多的适合bcrypt密码输入)。 在用完熵之前,盈亏平衡点约为223个字符。
我们再说一遍。 对于这类密码,预散列明显增加了安全性。
回到现实世界
这些熵计算在真实世界中并不重要。 重要的是猜测熵。 这直接影响了攻击者的行为。 这就是你想要最大化的。
虽然很少有研究已经进入了猜测熵的过程,但我想指出一些观点。
随机猜测连续72个正确字符的几率非常低。 你有可能赢得强力球彩票21次,而不是发生这种冲突......这是我们谈论的数字中的一个大数字。
但我们可能不会在统计上绊跌。 在短语的情况下,前72个字符的机会是相同的,比随机密码高很多。 但它仍然非常低(根据每个角色2.3比特,你更有可能赢得5次强力球彩票)。
几乎
实际上,这并不重要。 有人猜测前72个字符是正确的,后面的字符会产生显着差异的可能性非常低,所以不值得担心。 为什么?
好吧,让我们假设你正在说一个短语。 如果这个人可以正确地获得前72个字符,他们可能真的很幸运(不太可能),或者这是一个常见的短语。 如果这是一个常见的短语,唯一的变量是多久才能做出来。
我们举个例子吧。 让我们从圣经中引用一段话(仅仅因为它是长文本的一个常见来源,而不是其他原因):
你不应该co your邻居的房子。 你不可贪恋你的邻居的妻子,或他的男仆或女仆,他的牛或驴,或任何属于你邻居的东西。
这是180个字符。 第73个字符是neighbor's
g
。 如果你猜到了这么多,你很可能不会停留在nei
,而是继续其余的经文(因为密码很可能会被使用)。 因此,你的“散列”没有增加太多。
顺便说一句:我绝对不主张使用圣经的报价。 事实上,完全相反。
结论
你不会真的帮助那些通过哈希算法使用长密码的人。 一些团体,你绝对可以帮忙。 有些你肯定会受伤。
但最终没有一个过于重要。 我们正在处理的数字只是太高 。 熵的差别不会太大。
你最好离开bcrypt。 你更有可能搞砸哈希(字面上,你已经做到了,而你不是第一个也不是最后一个犯这个错误的),而不是你试图阻止的攻击发生。
专注于保护网站的其他部分。 并在注册时在密码框中添加密码熵表以指示密码强度(并指出密码是否过长,用户可能希望更改密码)...
那是我的0.02美元(或者可能超过0.02美元)......
至于使用“秘密”辣椒:
实际上并没有研究将一个散列函数加入到bcrypt中。 因此,如果将“peppered”散列hash1(hash2($value))
到bcrypt中会导致未知漏洞(我们知道hash1(hash2($value))
可能会在碰撞抵抗和原像攻击的周围暴露重大漏洞hash1(hash2($value))
,但目前还不清楚。
考虑到你已经在考虑存储一个秘密密钥(“胡椒”),为什么不以一种被充分研究和理解的方式来使用它? 为什么在存储之前不加密哈希?
基本上,在散列密码之后,将整个散列输出提供给强加密算法。 然后存储加密的结果。
现在,SQL注入攻击不会泄漏任何有用的东西,因为它们没有密钥。 如果密钥被泄露,攻击者并没有比使用普通散列(这是可证明的,用胡椒“预散列”不提供的东西)更好。
注意:如果您选择这样做,请使用库。 对于PHP,我强烈推荐Zend Framework 2的ZendCrypt
包。 这实际上是我目前在这个时间点推荐的唯一一个。 它受到了强烈的评论,它为你做出了所有的决定(这是一件非常好的事情)......
就像是:
use ZendCryptBlockCipher;
public function createHash($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
return $blockCipher->encrypt($hash);
}
public function verifyHash($password, $hash) {
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
$hash = $blockCipher->decrypt($hash);
return password_verify($password, $hash);
}
这是有益的,因为你正在使用所有的算法,这些算法都被很好地理解和深入研究(至少相对)。 记得:
任何人,从最无知的业余人员到最好的密码专家,都可以创建一个他自己无法破解的算法。
Peppering密码确实是一件好事,但让我们看看为什么。
首先,我们应该回答胡椒何时有用的问题。 胡椒只保护密码,只要它保密,所以如果攻击者有权访问服务器本身,那么它是没有用的。 一个更容易的攻击是SQL注入,它允许对数据库的读取访问(对我们的散列值),我准备了一个SQL注入演示来展示它是多么容易(点击下一个箭头获得准备输入)。
那么胡椒究竟有什么帮助? 只要胡椒保持秘密,它可以防止字典攻击中的弱密码。 密码1234
然后会变成类似1234-p*deDIUZeRweretWy+.O
。 这个密码不仅长得多,而且还包含特殊字符,并且永远不会成为任何字典的一部分。
现在我们可以估计用户将使用哪些密码,可能更多的用户将输入弱密码,因为有64-72个字符的密码(实际上这将是非常罕见的)。
另一点是暴力的范围。 sha256散列函数将返回256位输出或1.2E77组合,对于暴力破解来说,即使对于GPU(如果我正确计算,2013年GPU上需要大约2E61年)也是如此。 所以我们在胡椒应用上并没有真正的缺点。 由于散列值不是系统化的,因此无法加速使用常见模式的暴力破解。
PS据我所知,72字符限制是特定于BCrypt本身的算法。 我发现的最佳答案是这样的。
PPS我认为你的例子有缺陷,你不能用完整的密码长度生成哈希,并用截断的验证它。 你可能打算以相同的方式应用胡椒来生成哈希和验证哈希。
Bcrypt使用基于昂贵的Blowfish密钥设置算法的算法。
bcrypt推荐的56字节密码限制(包括空终止字节)与Blowfish密钥的448位限制有关。 超出该限制的任何字节都不会被完全混合到结果散列中。 因此,当考虑到由这些字节产生的散列的实际效果时,bcrypt密码的72字节绝对限制因此不太重要。
如果您认为用户通常会选择长度超过55个字节的密码,请记住,您可以始终增加密码扩展的轮次,以增加密码表违规情况下的安全性(尽管与增加额外密码相比,这要增加很多字符)。 如果用户的访问权限非常重要,以至于用户通常需要长时间的密码,那么密码到期时间也应该很短,如2周。 这意味着在黑客投入资源来击败测试每个试用密码所涉及的工作因素以查看它是否会生成匹配的哈希值时,密码不太可能保持有效。
当然,在密码表没有被破坏的情况下,我们应该只允许黑客在锁定用户账户之前最多尝试10次来猜测用户的55字节密码;)
如果您决定预先散列超过55个字节的密码,那么您应该使用SHA-384,因为它具有最大的输出而不会超过极限。
链接地址: http://www.djcxy.com/p/21599.html上一篇: How to hash long passwords (>72 characters) with blowfish