我想实现一种基于令牌的身份验证系统,该系统强烈抵抗内部攻击。也就是说,对代码和数据库具有只读访问权限的某人不应使用其他人的令牌。这意味着JWT并非可行之路,因为如果有人知道令牌签名的秘密密钥(存储在服务器上的纯文本中),他们可能会损害所有人的令牌(前提是他们知道用户的有效负载) )。此外,我想实现比OpenID / OAuth更简单的方法。
因此,我考虑将每个用户的访问令牌存储在数据库中。每个人的生存时间为1天。但是,如果您知道用户的令牌,那么1天就足以对它造成麻烦,例如删除帐户。因此,我想添加另一个安全层并哈希数据库中的令牌。
问题是,PHP的password_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_verify()
。但是,检索数据库的所有用户并为每个用户使用password_verify()
效率太低。
使用用户在其列中的散列值来查找用户(或更通常的数据库记录)的正确方法是什么?
谢谢您的帮助。
我看到2个解决方案:
1。使用没有盐的哈希函数
使用PHP hash()
函数,结果将始终与没有随机生成的盐相同。这样,您可以像这样比较两个令牌散列:
// 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中创建自己的password_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);