Fail to verify a manipulated token

Issue #399 resolved
leonL created an issue

Hi hello,

I created below code just based on the example code from the homepage, just trying to learn how to verify a token with public jwk or public key. While doing that i ran into something weird: even if i added one charactor at the end of the signature of the token, the token can still pass verification; if added more than one character, the verification is doing fine.

Tried with below scenarios:

(1)Any length of chars added at the beginning or in the middle of the signature, the verification is working fine

(2)One char added at the end of the signature, the verification is failing, the modified token can still pass verification

(3)More than one char added at the end of the signature, the verification is working fine

(4)Any length of chars added in header section or payload section at any position, the verification is working fine

The code is as below:

package token.jwt;

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;
import com.nimbusds.jwt.SignedJWT;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class Test {

   public static void main(String[] args) throws Exception {
      // Generate an EC key pair
      ECKey ecJWK = new ECKeyGenerator(Curve.P_256)
            .keyID("123")
            .generate();
      ECKey ecPublicJWK = ecJWK.toPublicJWK();

      // Create the EC signer
      JWSSigner signer = new ECDSASigner(ecJWK);

      // Creates the JWS object with payload
      JWSObject jwsObject = new JWSObject(
            new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(ecJWK.getKeyID()).build(),
            new Payload("Elliptic cure"));

      // Compute the EC signature
      jwsObject.sign(signer);

      // Serialize the JWS to compact form
      String s = jwsObject.serialize();

      // Manipulated the original token and use it later
      SignedJWT jwt = SignedJWT.parse(s + "X");

      // The recipient creates a verifier with the public EC key
      JWSVerifier verifier = new ECDSAVerifier(ecPublicJWK);

      // Verify the EC signature of the manipulated token
      assertTrue("ES256 signature verified", jwt.verify(verifier));
      assertEquals("Elliptic cure", jwt.getPayload().toString());
   }
}

And i am using version 5.4.1

Not sure if i missed anything or miss-understood anything.

Any suggestions on how to workaround this would be appreciated!

So many thanks,

leonL

Comments (12)

  1. Vladimir Dzhuvinov

    Hi Leon,

    What you witnessed is a case when a codec can allow for a trailing byte without affecting the underlying encoded bytes.

    For the ESxxx verification there are two conversions: from 1) BASE64URL -> 2) concat byte arrays padded to same length -> 3) DER encoding.

    I added a quick check to reject ESxxx sigs which length doesn’t match the expected for the alg (for ES256 this is 64 bytes for instance). This doesn’t change the DER encoding, but it does change the concatenated byte arrays length to 65 and can be detected.

    Commit: d522806

    Note, for the ES384 and ES512 algs adding an extra char to the sig BASE64URL cannot be detected. I suppose this will scare you 🙂 If you take the sig BASE64URL of ES384 and get its byte array, then add an extra char and repeat, the decoded byte arrays will be identical. This is simply the nature of the BASE64 codec :)

    test ->

    https://bitbucket.org/connect2id/nimbus-jose-jwt/commits/d5228062b6e957a91521d9c2e90fac0fce52db49#Lsrc/test/java/com/nimbusds/jose/crypto/ECDSATranscodingTest.javaT77

    This has come up in other libs:

    Bug or question: Altering last meaningful character in base64 string doesn't change decoded outcome #65

    https://github.com/jwt-dotnet/jwt/issues/65

  2. leonL reporter

    And regarding “Note, for the ES384 and ES512 algs adding an extra char to the sig BASE64URL cannot be detected. I suppose this will scare you 🙂 If you take the sig BASE64URL of ES384 and get its byte array, then add an extra char and repeat, the decoded byte arrays will be identical. This is simply the nature of the BASE64 codec :)“

    Actually i noticed these during the investigation, just not so sure whether this is possible for any form of attacks. Now as i understand this doesnt seem to be a security issue since the header and payload can never be manipulated plus adding any length of extra to another position in the BASE64URL signature will also break the verification, is it correct?

    Thanks again!

    -Leon

  3. Log in to comment