如何在PHP中检查没有密钥的JWT完整性



我开发了一个应用程序,该应用程序使用了一些非它生成的JWT令牌。到目前为止,我存储令牌,并严格比较头中给出的令牌和数据库中得到的令牌。

我发现的所有JWT PHP库都按以下顺序显示示例:

  1. 使用密钥ky生成令牌
  2. 使用此密钥解码JWT

我需要检查JWT是否有效(关于给定的标头、有效负载和签名),这将添加一个安全层。

我怎么能"简单地";在没有密钥的情况下检查令牌的完整性?

JWT可以使用对称不对称算法进行加密或者签名(或者两者都按顺序)。

您描述的过程使用对称算法,使用相同的密钥对令牌进行签名或加密,然后对其进行验证或解密。这适用于由同一应用程序创建和使用的令牌,例如,web应用程序向浏览器发送令牌,并从请求中读取令牌。

如果一个应用程序正在创建要由另一个应用使用的令牌,则应使用不对称算法:

  • 加密内容,您需要一个公钥,应用加密算法,最终得到一个只能用私钥解密的字符串。一个常见的类比是,公钥的作用就像挂锁:任何人都可以把它扣上,但只有私钥持有者才能在之后打开它
  • 进行签名,您需要一个私有密钥,应用签名算法,最终得到一条任何人都可读的消息,并且可由任何使用公钥的人验证。类似的情况是,公钥就像某人签名的样本:仅仅伪造他们的签名是不够的,但足以判断签名是否真实

在您的情况下,您需要生成令牌的应用程序使用私钥对其进行签名,只有该应用程序知道私钥。然后,相应的公共密钥可以共享给服务的每个用户,并存储在需要验证令牌的应用程序的配置中。

你没有指定你使用的库,所以很难具体说明,但你需要寻找的是对令牌进行签名而不是加密的选项。然后你(或控制其他应用程序的人)将公共密钥分发给每个需要验证它的端点,并验证它是否包含具有预期算法和密钥的签名。

例如,使用lcobucci/jwt库,创建令牌的应用程序如下所示:

use LcobucciJWTBuilder;
use LcobucciJWTSignerKey;
use LcobucciJWTSignerRsaSha256;
$signer = new Sha256();
$privateKey = new Key('file://{path to your private key}');
$token = (new Builder())->withClaim('some', 'claim')
->getToken($signer,  $privateKey);

收到它的应用程序会这样验证:

use LcobucciJWTParser;
use LcobucciJWTSignerKey;
$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的理念是,您不需要任何先验知识,而是依赖于检查令牌中的签名和声明(例如,使用"到期时"声明来避免攻击者重复使用他们在日志中找到的旧令牌)。这就减少了令牌的长度和复杂性,从而允许去中心化和无状态的应用程序读取令牌。

请找到我的代码片段来解码没有密钥和任何库的负载,简单的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密钥)端点的url,从中可以获得JWK形式的公钥。

而且,如果你只想读取令牌的内容,假设你谈论的是签名(而不是加密)的令牌,你可以通过简单的解码来访问内容。标头和有效载荷只是Base64url编码的。解码它们不需要密钥。

最新更新