Introduction

According to the JWS (JSON Web Signature) specification, only the alg header parameter is mandatory. In practice, however, JWT headers often contain several other parameters. The following ones are of particular interest to attackers.

1) JWK (JSON Web Key)

This stands for JSON Web Key, this header embeds the cryptographic key directly within the JWT header as a JSON object.

Example:

{
  "alg": "RS256",
  "jwk": {
    "kty": "RSA",
    "n": "... (modulus)",
    "e": "... (public exponent)"
  }
}

2) JKU (JSON Web Key Set URL)

This header provides a URL pointing to a server that hosts a set of keys, allowing the receiver to fetch the appropriate key.

Example:

{
  "alg": "RS256",
  "jku": "https://your-server.com/keys.json"
}

This header provides a dynamic way to manage public keys used for JWT signature verification. Instead of embedding the key within the JWT (jwk), the server can fetch the appropriate key from a centralized location.

Benefits:

  • Scalability: Supports multiple signing keys, allowing for role-based access control or key rotation.
  • Reduced JWT Size: Excludes the public key, making the JWT more compact.
  • Centralized Key Management: Updates to signing keys are reflected in the JWK Set, simplifying key changes.

Key rotation in JWKS means that after periodic interval of time the set of the keys being used is changed so that even if the attacker get a valid set of keys, the key set becomes invalid after sometime hence the attacker can not forge own set of tokens.

3) Kid ( key ID)

  • This header works in conjunction with jku to identify the specific key within the JWK Set that should be used for signature verification.
  • Example: If the JWK Set at the jku URL contains multiple keys, each with a unique kid value, the JWT header can specify the corresponding kid to indicate which key to use.
  • This is not the secret key itself but it is a tag that points to the actual key in the set of keys that is to be used.

NOTE: Each of these headers have their own security considerations which will be studied further.

Injecting self-signed JWTs via the jwk parameter

Structure of JWK

The structure of the public key in the jwk header is:

{
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
        "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
    }
}

The meaning of the various terms is:

  • kid (optional):

Value: "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG"

This is a key identifier (optional in the JWS specification but often included). It's not strictly necessary here because there's only one key, but it could be used if there were multiple keys in the JWT.

  • typ (optional):

Value: "JWT"

This optional field indicates the type of the token, which is "JWT" in this case.

  • alg (mandatory):

Value: "RS256"

This mandatory field specifies the cryptographic algorithm used to sign the JWT. Here, it's "RS256," which indicates the use of the RSA algorithm with a 256-bit key size.

  • jwk (optional):

This optional parameter contains the public key information used for signature verification. Since it's present here, the JWT doesn't rely on fetching a key from a separate location using jku.

  • kty (mandatory):

Value: "RSA"

This mandatory field within the jwk object indicates the key type, which is "RSA" in this example.

  • e (mandatory):

Value: "AQAB" (Base64-encoded public exponent)

This mandatory field represents the public exponent used in the RSA algorithm. It's encoded in Base64 URL encoding.

  • kid (optional - repeated):

Value: "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG" (same as the top-level kid)

This optional field within the jwk object is a repetition of the top-level kid for potential future use with multiple keys.

  • n (mandatory):

Value: "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m" (Base64-encoded modulus)

This mandatory field represents the public modulus used in the RSA algorithm. It's also encoded in Base64 URL encoding.

NOTE: The public key itself is the combination of the modulus (n) and the public exponent (e).

Security Issue

The JSON Web Signature (JWS) specification describes an optional jwk header parameter, which servers can use to embed their public key directly within the token itself in JWK format. This tells the application which key to use to verify the JWT and if verified then only provide the access to any resource.

Ideally, servers should only use a limited whitelist of public keys to verify JWT signatures. However, misconfigured servers sometimes use any key that's embedded in the jwk parameter.

This is a security issue since the server is not configured properly and can accept any key as the valid public key.

In such a case the attacker can exploit this behavior by signing a modified JWT (means changing the payload or other information of the JWT) using their own RSA private key, then embedding the matching public key in the jwk header and then change the kid parameter also in the header part of JWT. This will then fool the server to accept this forged JWT and thus give, say admin access to the attacker.

Now to demonstrate this security issue, next is the walk through, a step by step solution for web security academy lab, "Lab: JWT authentication bypass via jwk header injection AUTOMATED METHOD". There are two ways of solving this lab one is the automated way and other one is manual way.

Lab: JWT authentication bypass via jwk header injection (AUTOMATED METHOD)

Lab Description

This lab uses a JWT-based mechanism for handling sessions. The server supports the jwk parameter in the JWT header. This is sometimes used to embed the correct verification key directly in the token. However, it fails to check whether the provided key came from a trusted source.

To solve the lab, modify and sign a JWT that gives you access to the admin panel at /admin, then delete the user carlos.

You can log in to your own account using the following credentials: wiener:peter

Solution to the lab

Begin the lab by logging in using the given credentials, this helps us capture the request in burp suite that contains a JWT.

None

As can be seen in the screenshot below, the request with captured JWT is visible. For further work on the request, we send it to the repeater.

None
None

As visible in the above screenshot, in the "JSON Web Token" section of repeater we edit the token and change the sub parameter of payload from "wiener" to "administrator".

Further we change the path in the request to "/admin" as shown in the image below so as to access the admin panel upon sending the request.

None

In this step we go to the JWT editor tab and generate a new RSA key pair with which we will sign this JWT. We use the same kid as mentioned in the JWT so that there is no hassle of changing the kid parameter in the header.

None
None

We paste the same "kid" that is key id as it was there in the JWT and finally generate the RSA key pair.

None
None

We now visit the JWT again and click on the attack button, there we select the option of "embedded JWK" (screenshot below)

None

This displays a dialog box where we select the key with which the JWT will be signed.

None

After signing the JWT with this key, this is what we get:

None

The header of the JWT now has an embedded JWK and that the token has been signed with the key we generated. We finally send the request and as can be seen in the screenshot below we have got access to the admin panel.

None

This means that there was this vulnerability where the JWT is accepting any key (which it should not) in the JWK parameter, this allows the attacker to sign the token with their own key and then embed the public key in the JWK and eventually get access to say admin panel as in this case.

Now in this step we find the path using which we can delete the user carlos as shown below in the screenshot below.

None

Copy this path and replace the "/admin" with this path and send the request.

None

Sending this request finally solves the lab.

None

The next part delves into JKU and its security implications along with demonstration to solve this lab using the manual way.