首页 > 解决方案 > 通过哈希令牌查找用户

问题描述

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

因此,我考虑为存储在数据库中的每个用户设置访问令牌。他们每个人的生存时间为 1 天。但是如果你知道一个用户的令牌,1 天就足以用它做一些讨厌的事情,例如删除帐户。因此,我想添加另一个安全层并对数据库中的令牌进行哈希处理。

问题是,password_hash()PHP 的函数使用盐,所以它不是确定性的。这意味着我不能这样做:

// 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()为每个用户使用会太低效。

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

感谢您的帮助。

标签: phpsqlhashaccess-tokensalt

解决方案


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

Salting 和 key-stretching 是针对弱用户密码的措施,以防止字典攻击并使暴力攻击不切实际。强随机令牌可以在没有这种预防措施的情况下进行散列。

我写了一些示例代码,如何生成这样的令牌。


推荐阅读