Php 通过哈希标记查找用户

Php 通过哈希标记查找用户,php,sql,hash,access-token,salt,Php,Sql,Hash,Access Token,Salt,我想实现一个基于令牌的身份验证系统,它能够强烈抵抗内部攻击。也就是说,对代码和数据库具有只读访问权限的人应该不能使用其他人的令牌。这意味着JWT不是一条出路,因为如果有人知道令牌签名(以明文形式存储在服务器上)的密钥,他们就可以破坏每个人的令牌(前提是他们知道用户的有效负载)。此外,我希望实现比OpenID/OAuth更简单的东西 因此,我考虑将每个用户的访问令牌存储在数据库中。他们每个人都有一天的生存时间。但是,如果你知道一个用户的令牌,1天就足够用它做一些讨厌的事情,比如删除帐户。因此,我想

我想实现一个基于令牌的身份验证系统,它能够强烈抵抗内部攻击。也就是说,对代码和数据库具有只读访问权限的人应该不能使用其他人的令牌。这意味着JWT不是一条出路,因为如果有人知道令牌签名(以明文形式存储在服务器上)的密钥,他们就可以破坏每个人的令牌(前提是他们知道用户的有效负载)。此外,我希望实现比OpenID/OAuth更简单的东西

因此,我考虑将每个用户的访问令牌存储在数据库中。他们每个人都有一天的生存时间。但是,如果你知道一个用户的令牌,1天就足够用它做一些讨厌的事情,比如删除帐户。因此,我想添加另一个安全层,并在数据库中散列令牌

问题是,PHP的
password\u hash()
函数使用salt,因此它不是确定性的。这意味着我不能这么做:

// 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\u verify()
。但是检索数据库的所有用户并为每个用户使用
password\u verify()
,效率太低了

在用户的列中使用哈希值查找用户(或者更一般地说是数据库记录)的正确方法是什么

感谢您的帮助。

我看到了两种解决方案:

1。使用不含盐的哈希函数

使用PHP
hash()
函数,结果将始终相同,因为没有随机生成的salt。通过这种方式,您可以比较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。在SQL中创建自己的
密码\u verify()

您可以在数据库中编写自己的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);

正如您已经发现的,不能使用salt来创建可搜索的哈希。不过,只要您的令牌非常强,使用SHA-256这样的快速非盐算法进行散列就非常好。这意味着标记应至少包含24个字母表的随机字符
0..9、a..z、a..z

salt和密钥拉伸是针对弱用户密码的措施,以防止字典攻击,并使暴力攻击变得不切实际。强随机标记可以在没有这种预防措施的情况下进行散列


我写了一些关于如何生成这样的令牌的文章。

首先为什么要这样做?用户密码在许多用户中都是相同的,对吗?我想简化我的问题,但事实上我不会
password\u hash()
passwords,而是访问令牌,存储在数据库中且在给定2个不同用户时安全唯一的。因此,我想知道我收到的访问令牌是否有效。我不想以纯文本的形式存储它们,因为我希望我的身份验证系统非常安全。啊,好吧,这一点也很重要,因为密码的取消处理,然后在查询中使用它似乎是可疑的。您发布的代码将不起作用。您正在尝试使用哈希函数而不是要验证的函数进行选择。如果这不是你真正使用的,你将不得不编辑。这对我来说,让我不清楚。我知道它不起作用,我在之前写下了“我不能那样做”。