Найти пользователя по его хешированному токену - PullRequest
0 голосов
/ 20 октября 2019

Я хочу внедрить систему аутентификации на основе токенов, которая будет устойчива к атакам инсайдеров. То есть тот, кто имеет доступ только для чтения к коду и базе данных, не должен иметь возможность использовать чужой токен. Это означает, что JWT - это не тот путь, потому что, если кто-то знает секретный ключ подписей токенов (который хранится в виде простого текста на сервере), они могут скомпрометировать токены всех (при условии, что они знают полезную нагрузку пользователя). ). Кроме того, я хочу реализовать нечто более простое, чем OpenID / OAuth.

Таким образом, я подумал о том, чтобы иметь маркеры доступа для каждого пользователя, хранящегося в базе данных. Каждый из них будет иметь продолжительность жизни 1 день. Но если вы знаете маркер пользователя, одного дня более чем достаточно, чтобы сделать что-то неприятное, например, удалить учетную запись. Поэтому я хочу добавить еще один уровень безопасности и хэшировать токены в базе данных.

Проблема в том, что функция PHP * password_hash() использует соль, поэтому она не является детерминированной. Это означает, что я не могу этого сделать:

// Insert a new user
$token = generateToken(size=32);
$tokenHash = password_hash($token, PASSWORD_DEFAULT);
$sql = "
INSERT INTO `User` (username, password_hash, token_hash)
VALUES (:username, :password_hash, :token_hash)
";
$stmt->prepare($sql);
$stmt->execute([
  ":username" => $username,
  ":password_hash" => $paswordHash
  ":token_hash" => $tokenHash
]);

// Find an user with their token
$tokenHash = password_hash($token, PASSWORD_DEFAULT);
$sql = "SELECT * FROM `User` WHERE token_hash = :token_hash";
$stmt = $db->prepare($sql);
$stmt->execute([":token_hash" => $tokenHash]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

Действительно, $tokenHash на практике никогда не будет равно значению, хранящемуся в базе данных, и правильная функция для этого сравнения - password_verify(),Но поиск всех пользователей базы данных и использование password_verify() для каждого из них было бы слишком неэффективным.

Как правильно найти пользователя (или, в более общем случае, запись базы данных), используя хешированное значениев своих колонках?

Спасибо за помощь.

Ответы [ 2 ]

1 голос
/ 23 октября 2019

Как вы уже узнали, нельзя использовать соль для создания поисковых хэшей. Хэширование с помощью быстрого несоленого алгоритма, такого как SHA-256, прекрасно, хотя , если ваши токены очень сильны . Это означает, что токен должен содержать не менее 24 случайных символов алфавита 0..9, a..z, A..Z.

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

Я написал пример кода , как можно генерировать такие токены.

0 голосов
/ 20 октября 2019

Я вижу 2 решения:

1. Используйте хеш-функцию, в которой нет соли

С функцией PHP hash() результат всегда будет таким же, как и случайным образом созданная соль. Таким образом, вы можете сравнить 2 хеша токенов следующим образом:

// Insert a new user
$token = generateToken(size=32);
$tokenHash = hash("sha256", $token); // Use hash() instead of password_hash()
$sql = "
INSERT INTO `User` (username, password_hash, token_hash)
VALUES (:username, :password_hash, :token_hash)
";
$stmt->prepare($sql);
$stmt->execute([
  ":username" => $username,
  ":password_hash" => $paswordHash
  ":token_hash" => $tokenHash
]);

// Find an user with their token
$tokenHash = hash("sha256", $token); // Same here
$sql = "SELECT * FROM `User` WHERE token_hash = :token_hash";
$stmt = $db->prepare($sql);
$stmt->execute([":token_hash" => $tokenHash]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

2. Создайте свой собственный password_verify() в SQL

Вы можете написать свою собственную функцию проверки bcrypt в базе данных и использовать ее во втором запросе:

// Find an user with their token
$sql = "SELECT * FROM `User` WHERE password_verify(:token, token_hash)";
$stmt = $db->prepare($sql);
$stmt->execute([":token" => $token]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
...