使用 crypt() 和 password_hash() 函数加密后密码不匹配


Password does not match after being encrypted using crypt() and password_hash() function

我修改了我的旧帖子。我尝试了crypt()函数,现在尝试使用password_hash()和password_verify()来验证来自数据库的加密密码,但是在每次调用时,password_hash()函数都会重新调整不同的加密字符串,password_verify()无法匹配它。

这就是我这样做的方式。

 //please ignore the syntax error if any
$data = '11';
$dbpass = password_hash($data, PASSWORD_BCRYPT);
echo $dbpass;  // displays the random strings on each page refresh.

一旦密码保存到数据库中,在登录过程中不会匹配。以下是我的实际功能。

   private function process_data($password){
    $password = __STR.$password.__STR;
    return  password_hash($password, PASSWORD_BCRYPT);
  }
  private function processed($login_password, $dbpassword){
    $login_password = __STR.$login_password.__STR;
    return password_verify($login_password, $dbpassword);
  }

在每次调用用于为密码创建哈希字符串的函数时,该函数下次都会返回不同的字符串。

好的,让我们一一介绍一下。

首先,它是散列,而不是加密。加密是双向的,散列是单向的。我们想要散列。我们永远不想加密。是的,术语很重要。请使用正确的术语。

接下来,对password_hash的每个调用都应该返回不同的哈希。那是因为它会产生强烈的随机盐。这就是它的设计方式,以及你真正应该如何使用它。

此外,不要做在密码前后添加__STR的"胡椒"事情。您什么也没做,只是可能会削弱用户密码(这不好)。如果您想了解有关为什么这是一个坏主意的更多信息:请阅读此答案。

继续,我强烈建议您不要直接使用crypt。实际上,搞砸并生成极弱的哈希值非常容易。这就是设计password_* API 的原因。 crypt是低级库,您希望在代码中使用高级库。有关搞砸bcrypt的方法的更多信息,请查看我的博客:搞砸Bcrypt的七种方法。

密码 API 旨在成为一个简单的一站式商店。如果它不适合您,请检查以下事项:

  1. 您是否使用的是 PHP>= 5.5.0?或者您是否将 PHP>= 5.3.7 与 password_compat一起使用?

    1. 您的数据库列是否足够宽?

      它需要至少为 60 个字符长。

    2. 您是否正在检查函数的结果是字符串,而不是bool(false)

      如果存在内部错误,它将从 password_hash 返回一个非字符串。

    3. 你收到任何错误吗?

      您是否将error_reporting打开到其最大设置(我建议-1捕获所有内容)并检查代码是否没有抛出任何错误?

    4. 您确定正确使用它吗?

      function saveUser($username, $password) {
          $hash = password_hash($password, PASSWORD_BCRYPT);
          // save $username and $hash to db
      }
      function login($username, $password) {
          // fetch $hash from db
          return password_verify($password, $hash);
      }
      

      请注意,每个只能调用一次。

  2. 您是否将 PHP <5.3.7 与 password_compat一起使用?如果是这样,这是您的问题。您正在不受支持的 PHP 版本上使用兼容性库。您可以让它工作(某些 RedHat 发行版已经向后移植了必要的修复程序),但您使用的是不受支持的版本。请升级到合理的版本。

如果所有其他方法都失败了,请尝试运行此代码并报告输出:

$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$test = crypt("password", $hash);
$pass = $test == $hash;
echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
echo "'n";

如果返回 Fail ,则您正在运行不受支持的 PHP 版本,应该升级。如果它返回 pass,则错误在您的逻辑中的某个地方(库运行良好)。

存储密码的最佳方法是使用 PHP 的函数 password_hash()。它会自动为每个密码生成一个加密安全的盐,并将其包含在生成的 60 个字符的字符串中。您完全不必担心盐!

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

自己的方案非常弱,首先你使用MD5,这对于生成密码哈希来说太快了,然后你使用静态盐,这违背了盐的目的。也许您想看看我关于安全存储密码的教程。

编辑以回答更新的问题:

没有必要将

__STR添加到密码中(如果你想添加一个辣椒,有更好的方法),但你的示例函数应该实际工作。由于随机盐,password_hash() 的返回值每次都会不同。这是正确的,函数password_verify()能够提取这种盐进行验证。在您的情况下,数据库字段可能是问题所在。确保它可以容纳 60 个字符的字符串。