为什么php的password_hash这么慢?

时间:2015-05-13 13:07:28

标签: php password-encryption password-hash

我使用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

4 个答案:

答案 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。该盐可以与散列密码一起以纯文本形式存储在数据库中。为每个密码使用不同的盐会将其添加到密码中,因此潜在的攻击者必须为数据库中的每个盐生成彩虹表。此时,攻击者可能会利用其他技术来获取密码而不是数据库泄露。