Как сохранить хэш пароля, чтобы сопоставить уже хешированный пароль?

Я реализую функцию «запомнить меня» (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()

// 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');
}

Из функции Initialize() AppController:

// 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, никогда не будет найден.

Проблема в том, что два хэша должны совпадать? Или я должен использовать функцию для сопоставления двух хэшей друг с другом?


person Simon    schedule 02.09.2019    source источник
comment
Хэши обычно солёные, иначе хэши паролей пользователей с таким же паролем легко идентифицировать; это слабость безопасности. Однако я думаю, что хэш файла cookie не совпадает, потому что это не его цель. Я думаю, что хэш файла cookie не позволяет злоумышленникам извлекать пароли из пользовательских файлов cookie. Может быть, клиент должен просто преобразовать хэш обратно в строку пароля и отправить его методу check()?   -  person Zimano    schedule 02.09.2019
comment
Вы можете изучить поведение с использованием пароля, которое обеспечивает проверка текущего пароля СУХИМ способом на основе вашей конфигурации. Если вы еще не используете новые плагины аутентификации.   -  person mark    schedule 03.09.2019


Ответы (1)


Прежде всего, вы можете взглянуть на новый плагин аутентификации, он поставляется с аутентификатором cookie из коробки!

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

При этом ваша предпосылка неверна, тот факт, что два хэша не совпадают, обусловлен дизайном и правильным поведением, и нет метода, который мог бы их сопоставить.

Что вам нужно сделать, так это сохранить уникальный идентификатор в вашем файле cookie, скажем, имя пользователя, вместе с хэшем, созданным из вторых учетных данных, хешем, который вам нужно создать снова при попытке входа в систему через данные файла cookie. Скажем, например, хеш имени пользователя + хешированный пароль, хешированный пароль, потому что вам нужно это точное значение снова, чтобы воссоздать простое значение, чтобы сравнить его с хэшем, хранящимся в файле cookie, т.е. вы не можете (и в любом случае не должны) использовать простой пароль, так как он больше не будет доступен при последующих запросах.

Затем, используя данные 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 3.x нет метода login() AuthComponent, метод login() взят из CakePHP 2.x! Также в идеале вы должны обрабатывать все это в пользовательском объекте аутентификации и обработчике событий для события Auth.afterIdentify и держать логику вне контроллера.

Смотрите также

person ndm    schedule 02.09.2019
comment
Большое спасибо за быстрый ответ! У меня было ощущение, что это что-то в этом духе, но я не совсем понял суть. Я думаю, что единственная проблема с предложенным вами кодом, которую я обнаружил, заключалась в том, что вы использовали + вместо точки для конкатенации. И я также думаю, что $user-›username вместо этого должен быть элементом массива: $user['username'] - person Simon; 03.09.2019
comment
Ой, да, это должна быть точка. Доступ к объекту должен быть в порядке, хотя, если вы не отключите гидратацию, доступ к массиву также работает, поскольку сущности реализуют \ArrayAccess. @Саймон - person ndm; 03.09.2019