我使用password_hash
进行密码加密。然而,有一个奇怪的问题,password_hash
花费很长时间。这是一个示例代码。
此代码的成本将超过1秒。这是正常的吗?
<?php
$startTime = microtime(TRUE);
$password='123456';
$cost=13;
$hash=password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
password_verify($password,$hash);
$endTime = microtime(TRUE);
$time = $endTime - $startTime;
echo $time;
?>
结果是:1.0858609676361
答案 0 :(得分:13)
在3v4l上运行后似乎完全正常。
密码哈希不是您想要优化的东西。用Leigh on the hash
documentation:
如果你是为了安全而散列密码等,速度不是你的朋友。你应该使用最慢的方法。
缓慢哈希意味着缓慢破解,并且有望使像彩虹表这样的东西比它的价值更麻烦。
答案 1 :(得分:9)
password_hash
的默认算法bcrypt
设计为慢。
http://en.wikipedia.org/wiki/Key_stretching
在密码学中,密钥扩展指的是用于制作可能弱密钥的技术,通常是密码或密码短语,通过增加测试每个可能密钥所需的时间来抵御暴力攻击更安全。人类创建的密码或密码通常很短或可预测,足以让密码破解。按键拉伸使这种攻击更加困难。
http://en.wikipedia.org/wiki/Rainbow_table#Defense_against_rainbow_tables
另一项有助于防止预计算攻击的技术是关键的延伸。使用拉伸时,salt,密码和许多中间散列值会多次运行通过底层散列函数,以增加散列每个密码所需的计算时间。例如,MD5-Crypt使用1000迭代循环,该循环重复地将salt,密码和当前中间散列值反馈回底层MD5散列函数。用户的密码哈希是salt值(非秘密)和最终哈希的串联。额外的时间对于用户来说并不明显,因为他们每次登录时都必须等待几分之一秒。另一方面,拉伸会降低蛮力攻击的有效性,与迭代次数成比例,因为它减少了攻击者在给定时间范围内可以执行的计算次数。该原则适用于MD5-Crypt和bcrypt。它还大大增加了构建预计算表所需的时间,但在没有盐的情况下,这只需要进行一次。
一整秒可能有点长 - 你可以尝试将$cost
放一到两个,以使它更像是十分之一秒,这将保留有效的保护,同时使延迟不明显给你的用户。
答案 2 :(得分:5)
是的,这是正常的。这就是cost
参数的用途:它允许您调整迭代计数,根据需要使哈希更慢或更快。
您应始终尽可能慢地使哈希值尽可能快。原因是对密码哈希的唯一可行攻击是暴力破解。您希望使成本如此之大,以至于简单的暴力迫使所有可能的值变得过长。这是您开始使用密码哈希攻击者的唯一真正防御。
一整秒似乎令人望而却步。你应该稍微降低这个成本,最多保持在几百毫秒之内。根据需要调整目标系统。
答案 3 :(得分:0)
首先,password_hash
不是加密。
password_hash()使用强大的单向散列算法创建新密码哈希。 password_hash()与crypt()兼容。因此,crypt()创建的密码哈希值可以与password_hash()一起使用。
散列是单向的,无论你传递给它的是什么,总是会有相同的最终结果,但是你无法从散列中获得原始字符串。这是密码的理想选择,因为您希望存储用户密码的模糊版本,您可以在登录时轻松比较,而无需实际存储密码。这意味着如果数据库被泄露,只要密码被哈希,攻击者就不会获得密码,他们会得到基本无用的哈希密码(你可以使用彩虹表,我确定获得最终哈希的其他技巧,但需要花费相当多的努力。)
这会导致您的原始问题。为什么密码哈希缓慢?它们很慢,因为从散列中获取原始字符串的唯一方法之一是重新生成该散列。因此,如果生成每个哈希需要1秒钟,那么它就会比使用md5
版本sha
之类的快速哈希时更长。快速哈希对于除密码存储之外的所有内容都很有用。
希望这能回答你的问题。另外,我强烈建议为每个用户生成一个唯一的salt,并将其作为其中一个选项传递到password_hash
。该盐可以与散列密码一起以纯文本形式存储在数据库中。为每个密码使用不同的盐会将其添加到密码中,因此潜在的攻击者必须为数据库中的每个盐生成彩虹表。此时,攻击者可能会利用其他技术来获取密码而不是数据库泄露。