如何在没有数据库的情况下验证令牌?
How to validate a token without a database?
我在同一台服务器上托管了两个域,域 A 和域 B。
域 A 将生成对域 B 内容的唯一访问令牌。
域 A
<?php
//http://php.net/manual/en/function.phpversion.php
//echo 'Version of PHP: ' . phpversion();
session_start();
//$expiry_timestamp = time() + $expiry;
//https://davidwalsh.name/random_bytes //https://secure.php.net/random_bytes
//$token = bin2hex(random_bytes(64));
$token = bin2hex(openssl_random_pseudo_bytes(64));
//$time_token = 12000;
//$time_token = srand(floor(time() / $time_token));
//echo $token;
$_SESSION['token']=$token;
?>
<html>
<head>
</head>
<body>
<a href= "domainB.com/content1.php?token=<?php echo $_SESSION['token']; ?>">Content 1</a>
</body>
</html>
生成token的过程好像是对的,已经很容易生成了。
现在我的问题来了,如何验证从域 A 到域 B 生成的令牌?。生成的令牌必须只对生成令牌的内容有效,令牌不能对其他内容有效,令牌必须是唯一的,这样如果不是来自他或她的计算机,用户就不能与另一个用户共享访问权限,令牌必须仅在 4 小时访问后有效 4 小时后令牌将不再有效以显示内容必须生成新令牌才能再次访问。
是否可以在不使用数据库的情况下使用 cookie 完成此过程?
也许可以使用密钥来识别域 A 和 B,类似的东西
$APP_SECRET_KEY = "key code secret";
Json Web Token (JWT) 似乎符合您的要求。两个应用程序都使用一个密钥来交换令牌和加密数据。
示例用例:
- 让秘钥
$secret="secret"
- 原始数据告诉我们生成令牌时的 Unix 时间戳(
iat
字段)、用户 ID(sub
字段)和内容 ID(content
场)
$data = [
"sub" => "1234567890",
"iat" => 1516239022,
"content" => 1
];
应用程序 A 使用 HS256 算法 ($token = jwt_encode($raw, 'HS256', $secret)
) 使用密钥对原始数据进行编码。输出 $token
将是:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJjb250ZW50IjoxfQ.idM7d2fgmJVk3WjANwG-Gt6sY0lyE3eTvpKRpwITHRs
您可以解析令牌以在 JWT home page.
中查看其内容
令牌被发送到应用程序 B。此应用程序使用相同的算法和共享密钥 ($raw = jwt_decode($token, 'HS256', $secret)
) 对令牌进行解码。原始数据将在应用程序 B 中可用。此数据可用于验证令牌:
- 从
sub
字段中读取用户 ID 并检查它是否正确
- 从
content
字段中读取内容id并检查它是否正确
- 从
iat
字段读取token生成时的时间戳,检查是否在最近4小时内。
有several PHP libraries implement JWT个给你。
在这里使用共享密钥是一个很好的方法。
我倾向于使用HMAC when I need to generate and validate a token (e.g.: E-Mail verification) and don't want to store it in a DB. Plus, HMAC is built in to PHP,所以这里不需要库。
想法是,在您的数据之上,您添加一个签名以验证此令牌是由您的应用程序在域 A 上创建的。您在域 B 上再次以相同的方式生成令牌以验证它。
示例:
生成令牌的共享函数:
function buildVerificationToken($expires, $content)
{
// Same function on both domains
$APP_SECRET_KEY = 'key code secret'; // Maybe move that out of source code
$tokenData = [
'expires' => $expires, // Include it in signatur generation to prevent user from changing it in URL
'content' => $content, // Create different token for different content
'ip' => $_SERVER['REMOTE_ADDR'], // Identify the browser to make it not shareable. Best approach I could think of for this part.
];
$serialized = json_encode($tokenData);
return hash_hmac('sha256', $serialized, $APP_SECRET_KEY);
}
在域 A 上生成令牌:
<?php
$expires = time() + (4 * 3600); // +4h
?>
<a href= "domainB.com/content1.php?expires=<?php echo $expires; ?>&token=<?php echo buildVerificationToken($expires, 'content1'); ?>">Content 1</a>
在域 B 上验证它:
$providedExpires = (int) $_GET['expires'];
$providedToken = $_GET['token'];
$verificationToken = buildVerificationToken($providedExpires, 'content1'); // Build token the same way
if (!hash_equals($verificationToken, $providedToken)) { // hash_equals instead of string comparison to prevent timing attacks
// User provided forged token, token for another content, or another IP
die('Bad token'); // However you want to handle this
}
if (time() > $providedExpires) { // Check expiry time. We can trust the user did not modify it as we checked the HMAC hash
die('Token expired'); // However you want to handle this
}
// User is allowed to see content1
我在同一台服务器上托管了两个域,域 A 和域 B。
域 A 将生成对域 B 内容的唯一访问令牌。
域 A
<?php
//http://php.net/manual/en/function.phpversion.php
//echo 'Version of PHP: ' . phpversion();
session_start();
//$expiry_timestamp = time() + $expiry;
//https://davidwalsh.name/random_bytes //https://secure.php.net/random_bytes
//$token = bin2hex(random_bytes(64));
$token = bin2hex(openssl_random_pseudo_bytes(64));
//$time_token = 12000;
//$time_token = srand(floor(time() / $time_token));
//echo $token;
$_SESSION['token']=$token;
?>
<html>
<head>
</head>
<body>
<a href= "domainB.com/content1.php?token=<?php echo $_SESSION['token']; ?>">Content 1</a>
</body>
</html>
生成token的过程好像是对的,已经很容易生成了。
现在我的问题来了,如何验证从域 A 到域 B 生成的令牌?。生成的令牌必须只对生成令牌的内容有效,令牌不能对其他内容有效,令牌必须是唯一的,这样如果不是来自他或她的计算机,用户就不能与另一个用户共享访问权限,令牌必须仅在 4 小时访问后有效 4 小时后令牌将不再有效以显示内容必须生成新令牌才能再次访问。
是否可以在不使用数据库的情况下使用 cookie 完成此过程?
也许可以使用密钥来识别域 A 和 B,类似的东西
$APP_SECRET_KEY = "key code secret";
Json Web Token (JWT) 似乎符合您的要求。两个应用程序都使用一个密钥来交换令牌和加密数据。
示例用例:
- 让秘钥
$secret="secret"
- 原始数据告诉我们生成令牌时的 Unix 时间戳(
iat
字段)、用户 ID(sub
字段)和内容 ID(content
场)
$data = [
"sub" => "1234567890",
"iat" => 1516239022,
"content" => 1
];
应用程序 A 使用 HS256 算法 ($token = jwt_encode($raw, 'HS256', $secret)
) 使用密钥对原始数据进行编码。输出 $token
将是:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJjb250ZW50IjoxfQ.idM7d2fgmJVk3WjANwG-Gt6sY0lyE3eTvpKRpwITHRs
您可以解析令牌以在 JWT home page.
中查看其内容令牌被发送到应用程序 B。此应用程序使用相同的算法和共享密钥 ($raw = jwt_decode($token, 'HS256', $secret)
) 对令牌进行解码。原始数据将在应用程序 B 中可用。此数据可用于验证令牌:
- 从
sub
字段中读取用户 ID 并检查它是否正确 - 从
content
字段中读取内容id并检查它是否正确 - 从
iat
字段读取token生成时的时间戳,检查是否在最近4小时内。
有several PHP libraries implement JWT个给你。
在这里使用共享密钥是一个很好的方法。
我倾向于使用HMAC when I need to generate and validate a token (e.g.: E-Mail verification) and don't want to store it in a DB. Plus, HMAC is built in to PHP,所以这里不需要库。
想法是,在您的数据之上,您添加一个签名以验证此令牌是由您的应用程序在域 A 上创建的。您在域 B 上再次以相同的方式生成令牌以验证它。
示例:
生成令牌的共享函数:
function buildVerificationToken($expires, $content)
{
// Same function on both domains
$APP_SECRET_KEY = 'key code secret'; // Maybe move that out of source code
$tokenData = [
'expires' => $expires, // Include it in signatur generation to prevent user from changing it in URL
'content' => $content, // Create different token for different content
'ip' => $_SERVER['REMOTE_ADDR'], // Identify the browser to make it not shareable. Best approach I could think of for this part.
];
$serialized = json_encode($tokenData);
return hash_hmac('sha256', $serialized, $APP_SECRET_KEY);
}
在域 A 上生成令牌:
<?php
$expires = time() + (4 * 3600); // +4h
?>
<a href= "domainB.com/content1.php?expires=<?php echo $expires; ?>&token=<?php echo buildVerificationToken($expires, 'content1'); ?>">Content 1</a>
在域 B 上验证它:
$providedExpires = (int) $_GET['expires'];
$providedToken = $_GET['token'];
$verificationToken = buildVerificationToken($providedExpires, 'content1'); // Build token the same way
if (!hash_equals($verificationToken, $providedToken)) { // hash_equals instead of string comparison to prevent timing attacks
// User provided forged token, token for another content, or another IP
die('Bad token'); // However you want to handle this
}
if (time() > $providedExpires) { // Check expiry time. We can trust the user did not modify it as we checked the HMAC hash
die('Token expired'); // However you want to handle this
}
// User is allowed to see content1