如何保存密码散列以便匹配已经散列的密码?

How do I save a password hash so I can match an already hashed password?

我正在实施一项 "remember me" 功能 (CakePHP 3),该功能在 cookie 中存储用户名和散列密码。我的问题是我无法成功地将 cookie 中的散列密码与数据库中的散列密码进行匹配。

我正在使用 DefaultPasswordHasher 生成数据库哈希和 cookie 哈希。我最初认为这些会匹配,但事实并非如此。

我的第二个想法是 DefaultPasswordHasher 有一个函数可以验证两个哈希值是否来自同一个密码,但它的 check() 函数需要一个纯文本密码:https://api.cakephp.org/3.3/class-Cake.Auth.DefaultPasswordHasher.html

网上类似的问题似乎都适用于旧版本的 Cake 或者没有定论。

来自用户实体:

protected function _setPassword($password)
{
  if (strlen($password) > 0) {
    return (new DefaultPasswordHasher)->hash($password);
  }
}

来自 UsersController login() 函数

// Remember login?
if ($this->request->getData()['remember_me'] == "true") {
  // Hash the user's password
  $savedUser = [
    'password' => (new DefaultPasswordHasher)->hash($this->request->getData()['password']),
    'username' => $this->request->getData()['username']
  ]; 
  // Save login for 1 month
  $result = $this->Cookie->write('cookie_name', $savedUser, true, '1 month');
}

来自 AppController initialize() 函数:

// Load logged in user
$loggedInUserID = $this->Auth->user('id');

// If not logged in try cookie
if( !$loggedInUserID && $cookie = $this->Cookie->read('cookie_name') ) {
    error_log("Attempting to log in from cookie. Username: ".$cookie['username']);
    $user = $this->Users->find('all', [
        'conditions' => [
            'username' => $cookie['username'],
            'password' => $cookie['password'],
            'is_deleted' => 0
        ]
    ])
    ->first();

    // If a user was found, try to login
    if ($user) {
        if($this->Auth->login($user)) {
            error_log("Successfully logged in from cookie. Username: ".$cookie['username']);
            $loggedInUserID = $this->Auth->user('id');
        } else {
            error_log("Couldn't log in from cookie. Username: ".$cookie['username']);
            $this->redirect('/users/logout'); // destroy session & cookie
        }
    }
}

(为了清楚起见,我保留了临时错误日志消息。)

cookie 似乎保存正确,但其哈希值与数据库不匹配,这意味着从未找到与 cookie 匹配的用户。

问题是两个哈希应该匹配吗?或者我应该使用一个函数来匹配两个哈希值吗?

首先,您可能想看看新的身份验证插件,它随附开箱即用的 cookie 身份验证器!

https://book.cakephp.org/authentication/1.1/en/

也就是说,你的前提是错误的,两个哈希不匹配的事实是设计使然,并且是正确的行为,没有可以匹配它们的方法。

您应该做的是在您的 cookie 中存储一个唯一标识符,比方说用户名,以及从第二个凭据构建的散列,您需要在尝试登录时再次构建的散列通过 cookie 数据。比方说用户名的哈希值 + 哈希密码,hashed 密码,因为您需要再次使用该精确值来重新创建普通值,以便将其与存储在cookie,即您不能(也不应该)使用普通密码,因为它在后续请求中不再可用。

然后在使用cookie数据登录时,使用唯一标识符(用户名)查询用户,通过密码哈希器check()方法。

你给的例子看起来像这样(免责声明,这是未经测试的示例代码):

$user = $this->Users->get($this->Auth->user('id'));

$savedUser = [
    'username' => $user->username,
    'token' => (new DefaultPasswordHasher())->hash(
        $user->username . ':' . $user->password
    ),
];

// ...

查询并使用当前登录的用户,因此您必须在调用 $this->Auth->identify()!

之后执行此操作
// ...

$user = $this->Users
    ->find()
    ->where([
        'username' => $cookie['username'],
        'is_deleted' => 0,
    ])
    ->first();

if ($user) {
    $plain = $user->username . ':' . $user->password;
    if ((new DefaultPasswordHasher())->check($plain, $cookie['token'])) {
        // the hash matches, log the user in
        $user = $user->toArray();
        unset($user['password']); // but do not put the password in the session
        $this->Auth->setUser($user);
    } else {
        // the hash doesn't match, do something else
    }
}

注意setUser()的使用,CakePHP中没有login()方法3.xAuthComponentlogin()方法来自CakePHP2.x!同样理想情况下,您将在自定义身份验证对象和 Auth.afterIdentify 事件的事件处理程序中处理所有这些,并将逻辑置于控制器之外。

另见