【发布时间】:2020-12-26 23:37:49
【问题描述】:
我开发了一个应用程序,它使用了一些不是由它生成的 JWT 令牌。到目前为止,我存储令牌并严格比较标题中给出的令牌与我在数据库中得到的令牌。
我发现的所有 JWT PHP 库都按此顺序显示示例:
- 使用秘密 ky 生成令牌
- 使用此密钥解码 JWT
我需要检查 JWT 是否有效(关于给定的标头、有效负载和签名),这将添加一个安全层。
如何在没有密钥的情况下“简单地”检查令牌完整性?
【问题讨论】:
我开发了一个应用程序,它使用了一些不是由它生成的 JWT 令牌。到目前为止,我存储令牌并严格比较标题中给出的令牌与我在数据库中得到的令牌。
我发现的所有 JWT PHP 库都按此顺序显示示例:
我需要检查 JWT 是否有效(关于给定的标头、有效负载和签名),这将添加一个安全层。
如何在没有密钥的情况下“简单地”检查令牌完整性?
【问题讨论】:
JWT 可以是加密或签名(或两者,顺序),使用对称或非对称算法。
您描述的过程使用 对称 算法,其中使用相同的密钥对令牌进行签名或加密,然后对其进行验证或解密。这适用于由同一应用程序创建和使用的令牌 - 例如,Web 应用程序向浏览器发送令牌,并从请求中读取它。
如果一个应用程序正在创建令牌以供另一个应用程序使用,它应该使用 非对称 算法:
在您的情况下,您需要生成令牌的应用程序使用只有该应用程序知道的私钥签署它。然后可以将相应的 public 密钥共享给服务的每个用户,并存储在需要验证令牌的应用程序的配置中。
您没有指定您正在使用什么库,因此很难具体说明,但您需要寻找的是 sign 您的令牌的选项,而不是 encrypt它。然后您(或控制其他应用程序的任何人)将 public 密钥分发到需要验证它的每个端点,并验证它是否包含具有预期算法和密钥的签名。
例如,使用the lcobucci/jwt library,创建令牌的应用程序将如下所示:
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
$signer = new Sha256();
$privateKey = new Key('file://{path to your private key}');
$token = (new Builder())->withClaim('some', 'claim')
->getToken($signer, $privateKey);
接收它的应用程序会像这样验证它:
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Key;
$publicKey = new Key('file://{path to your public key}');
$token = (new Parser())->parse('{token taken from request}');
if ( $token->verify($signer, $publicKey) ) {
var_dump($token->getClaims());
}
else {
echo 'Token does not have valid signature!';
}
顺便说一句,如果您根据数据库中的已知列表检查令牌,您实际上并不需要 JWT,您可以使用 GUID 之类的东西并将额外的数据存储在数据库中。 JWT 的想法是您不需要任何先验知识,并依赖于检查签名和令牌内的声明(例如,使用“过期时间”声明来避免攻击者重用他们在日志中找到的旧令牌) .这权衡了令牌的长度和复杂性,以允许去中心化和无状态的应用程序读取令牌。
【讨论】:
请找我的sn-p解码payload,不用key,不用任何库,简单的php代码。
function decodeJWTPayloadOnly($token){
$tks = explode('.', $token);
if (count($tks) != 3) {
return null;
}
list($headb64, $bodyb64, $cryptob64) = $tks;
$input=$bodyb64;
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
$input = (base64_decode(strtr($input, '-_', '+/')));
if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
$obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
} else {
$max_int_length = strlen((string) PHP_INT_MAX) - 1;
$json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
$obj = json_decode($json_without_bigints);
}
return $obj;
}
【讨论】:
如何在没有密钥的情况下“简单地”检查令牌完整性?
假设问题意味着您根本无法访问任何类型的公钥(如果使用非对称签名算法)或秘密(如果使用对称算法),答案是:
你不能!
签名是一个哈希值,由您的标头 + 有效负载 + 密钥/密钥的值计算得出。验证基本上意味着重新计算该哈希并与令牌的签名进行比较。如果您没有密钥或秘密,则无法计算哈希,因此无法证明签名与内容是否匹配。
您没有详细介绍用例、令牌发行者的角色和您的应用程序。但是,如果您想验证由第三方身份验证系统颁发的令牌,它使用非对称算法,您可能会在令牌中找到密钥 id (kid) 和 JWK (JSON Web Key) 端点的 url ,您可以从中获取 JWK 形式的公钥。
而且,如果您只想读取令牌的内容,假设您正在谈论已签名(未加密)的令牌,那么您可以通过简单地解码来访问内容。标头和有效负载只是 Base64url 编码。您无需密钥即可对其进行解码。
【讨论】: