Reputation: 1126
I develop an application that uses some JWT tokens not generated by it. So far, I store tokens and strictly compare the token given in header with the one I got in database.
All JWT PHP libraries I found show examples in this order :
My need would be to check that the JWT is valid (regarding to header, payload and signature given) that would add a security layer.
How can I "simply" check the token integrity without having the secret key?
Upvotes: 1
Views: 3841
Reputation: 29
Please find my snippet to decode the payload without keys and without any libraries, simple php code.
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;
}
Upvotes: 1
Reputation: 22515
How can I "simply" check the token integrity without having the secret key?
Assuming that the question means that you simply don't have access to any kind of public key (in case a asymmetric signature algorithm was used) or a secret (in case of symmetric algorithms), the answer is:
You can't!
The signature is a hash, calculated from the values of your header + payload + secret/key. Verification basically means to recalculate that hash and compare with the signature of the token. If you don't have the key or secret, you can't calculate the hash and therefore not proof that the signature matches the content or not.
You didn't go into detail about the use case, the roles of the token issuer and your application. But if you want to verify a token that was issued by a third party authentication system, it uses a asymmetric algorthm and you might find a key id (kid
) and the url of a JWK (JSON Web Key) endpoint in the token, from which you can obtain the public key in form of a JWK.
And, if you just want to read the contents of the token, assuming that you're talking about signed (not encrypted) tokens, you can however asccess the content by simply decoding it. Header and payload are just Base64url encoded. You don't need a key to decode them.
Upvotes: 0
Reputation: 97718
JWTs can be either encrypted or signed (or both, sequentially), using either symmetric or asymmetric algorithms.
The process you describe uses a symmetric algorithm, where the same key is used to sign or encrypt the token, and then to verify or decrypt it. This would be appropriate for tokens that are being created and consumed by the same application - for instance, a web app sending a token to the browser, and reading it back from the request.
If one application is creating the token to be used by a different one, it should use an asymmetric algorithm:
In your case, you need the application generating the token to sign it using a private key, which only that application knows. The corresponding public key can then be shared to every user of the service, and stored in the configuration of the application that needs to verify the token.
You don't specify what library you're using, so it's hard to be specific, but what you need to look for is the option to sign your token rather than encrypt it. Then you (or whoever controls the other application) distribute the public key to every endpoint that needs to verify it, and verify that it contains a signature with the expected algorithm and key.
For instance, using the lcobucci/jwt
library, the application creating the token would look like this:
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);
and the application receiving it would verify it like this:
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!';
}
As an aside, if you're checking tokens against a known list in a database, you don't really need JWT, you can just use something like a GUID and store the extra data against that in the database. The idea of a JWT is that you don't need any prior knowledge, and rely on checking the signature and the claims inside the token (e.g. using an "expires at" claim to avoid an attacker reusing an old token they found in logs). This trades off the length and complexity of the token for allowing a decentralized and stateless application to read the tokens.
Upvotes: 3