0
votes

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 :

  1. Generate token with a secret ky
  2. Decode JWT with this key

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?

3

3 Answers

1
votes

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:

  • To encrypt something, you take a public key, apply the encryption algorithm, and end up with a string that can only be decrypted with the private key. A common analogy is that the public key acts like a padlock: anyone can snap it shut, but only the holder of the private key can open it afterwards.
  • To sign something, you take a private key, apply the signing algorithm, and end up with a message readable by anybody, and verifiable by anyone with the public key. The analogy is that the public key is like a sample of someone's signature: it's not sufficient to forge their signature, but it is sufficient to tell if a signature is real.

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.

0
votes

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.

0
votes

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;
}