如何保存密码哈希,以便可以匹配已经哈希的密码?

时间:2019-09-02 20:24:04

标签: authentication cakephp cookies cakephp-3.x

我正在实现“记住我”功能(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匹配的用户。

两个散列应该匹配的问题吗?还是我应该使用一个函数将两个散列彼此匹配?

1 个答案:

答案 0 :(得分:0)

首先,您可能想看一下新的身份验证插件,它附带了一个cookie身份验证器!

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

话虽这么说,您的前提是错误的,两个哈希值不匹配是事实,这是设计使然,行为正确,没有方法可以匹配它们。

您应该做的是在cookie中存储一个唯一的标识符,比如说用户名,以及根据第二个凭据构建的哈希,当您尝试通过以下方式登录时需要重新构建一个哈希: Cookie数据。例如,假设用户名+哈希密码,哈希密码的哈希,因为您再次需要该确切值来重新创建纯值,以便将其与Cookie中存储的哈希进行比较,即您不能(也无论如何也不应)使用普通密码,因为在以后的请求中将不再可用。

然后,当使用cookie数据登录时,使用唯一标识符(用户名)查询用户,并通过密码哈希器的check()方法将纯值(用户名+哈希密码)与cookie哈希进行比较

对于您给出的示例,可能看起来像这样(免责声明,这是未经测试的示例代码):

$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 3.x login()中没有AuthComponent方法,login()方法来自CakePHP 2.x!同样,理想情况下,您将在自定义身份验证对象中处理所有这些操作,并在Auth.afterIdentify事件中使用事件处理程序,并将逻辑排除在控制器之外。

另请参见