13

我修改了我的旧帖子。我尝试了 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);
  }

在每次为密码创建散列字符串的函数调用中,该函数下一次返回不同的字符串。

4

2 回答 2

45

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

首先,它是散列,而不是加密。加密是两种方式,散列是一种方式。我们想要散列。我们从不想加密。是的,术语很重要。请使用正确的术语。

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

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

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

Password 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 版本,应该升级。如果它返回通过,那么错误就在您的逻辑中(库运行正常)。

于 2013-10-31T14:17:48.867 回答
1

存储密码的最佳方式是使用 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 个字符的字符串。

于 2013-10-30T12:16:06.807 回答