7

I'm setting up a REST API with Auth0 as the authentication service. Everything is working but my confidence has been a bit shaken after a rather strange occurrence.

My implementation is based on the sample code here (The RS256 section) and here. The only modification being that I cast the PublicKey to an RSAPublicKey.

The issue is that I wanted to be positive that the verification would fail on a bad signature. I changed the signature's last character (we'll say "x") and the token still verified. BUT - switching it to any character other than "x" or the originally generated character caused it to fail as expected.

My suspicion is that this is due to some sort of padding/encoding/decoding/Base64 issue and that I just happened to pick a character with the same first n-number of bits or something? Of course, this means that if a successful "guess" were to be made, it would need to include the remaining forty-kabillion characters of the token - which is the whole point of its existence. So I'm not necessarrily concerned that the token will be guessable - I'm just making sure that I've implemented the gist of the verification correctly.

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.security.interfaces.RSAPublicKey;

public class Application {
    public static void main(String[] args) {

        try {
            JwkProvider provider = new UrlJwkProvider("<my-provider>");
            Jwk jwk = provider.get("<my-key-id>");

            String token = "<some-token-passed-from-client>";
            RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey();

            Algorithm algorithm = Algorithm.RSA256(publicKey, null);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("<my-issuer>")
                    .build();
            DecodedJWT jwt = verifier.verify(token);

        } catch (JWTVerificationException exception) {
            System.out.println("JWT Exception: " + exception.getMessage());
        } catch (JwkException e) {
            e.printStackTrace();
        }

    }
}
user2864874
  • 829
  • 1
  • 9
  • 21
  • where are you using private key in order to verfiy token – harsha kumar Reddy Aug 09 '17 at 05:37
  • @harshakumarReddy - The [documentation](https://github.com/auth0/java-jwt#pick-the-algorithm) states that when verifying a token, `null` can be passed in as the private key. – user2864874 Aug 09 '17 at 16:41
  • It can be null when you are using shared secret key and symmetric algorithum which you have to do in Auth 0 . since client is sending back token with every request without modifying – harsha kumar Reddy Aug 10 '17 at 08:54
  • I'm not sure I follow what you're saying. The documentation specifically states that the PK can be null "When using RSA...". – user2864874 Aug 10 '17 at 13:48
  • It is not clear when you say "I changed the signature's last character (we'll say "x") and the token still verified.". Token would be signed by private key, You need just public key to verify. Code looks correct. Did you manually changed something in JWT string ? – TechFree Jun 20 '19 at 12:51
  • Correct. Let's say that a token's base64-encoded signature is `abcd1234`. When I passed the token into `verifier.verify(token)`, it verified as expected (no exception thrown). However, when I manually changed the signature to `abcd123x` and then passed in the token with this modified signature, I expected verification to fail but the `.verify()` method didn't throw an exception. If I changed the signature to `abcd123z`, however (or any character other than `4` or `x`), an exception was thrown at verification as expected. – user2864874 Jun 23 '19 at 01:47
  • 2
    I should also mention that I haven't experienced the signature behavior in this or any other library since posting. I may try to reproduce if I have time in the near future and will report the bug if necessary. Otherwise will leave this as-is for posterity. – user2864874 Jun 23 '19 at 01:57

1 Answers1

0

Have a look no further than the JWT example (screenshot below).

The token has 3 parts in separate colors:

  • Header: Metadata about the token, including the algorithm used. Not signed and not verified.
  • Payload: Contents of the token (user claims). Signed and verified.
  • Signature: The signature of the token.

Headers in particular are not signed, they can contain almost anything and be altered. Although the token won't be able to be decoded if it's borked (bad algorithm value for example).

There are rules to encode, pad and serialize all this data together, refer to the specifications. It is possible to add/remove/edit few bytes in a token and still have a valid token, albeit slightly different (modified padding or headers). However it is not possible to alter the payload of a token, which is what's important.

JWT libraries offer separate functions to extract information from the token, like get_unverified_headers() and get_claims(), avoiding any potential confusion. Hypothetically if one were to accidentally read the user identifier from the header instead of the payload, it would be a critical vulnerability because that can be altered freely.

sample of JWT token

user5994461
  • 5,301
  • 1
  • 36
  • 57