Vulnerabilities in JWT libraries

JSON Web Tokens (JWTs) are commonly used for authorization purposes, since they provide a structured way to describe a token which can be used for access control. However, JWT libraries may contain flaws, and must be used in the correct way. The Capture the Flag event co-organized by Debricked at Lund University included examples of this problem.

JWTs are protected with either a digital signature or an HMAC, such that their contents cannot be manipulated. This makes them very useful in distributed or state-less scenarios, where the token may be issued by one entity, and then verified by another. Because of the integrity protection, the verifying party can be sure that the token has not been manipulated since it was issued.

A JWT consists of three parts: header, payload, and signature. The header and payload are both JSON objects, while the format of the signature part depends on information given in the header. Each part is base64url encoded, and separated by dots. An example of a JWT is:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDk5ODk3NzUsInVzZXIiOiJhbGljZSIsImlzX2FkbWluIjp0cnVlfQ.pBYy9s_j0qLotl8CFFFVid5CPOwm5aXl9Aq-uSfkFE4

where the first part is the header, which when decoded becomes the following JSON object:

{ 
    "alg": "HS256",
    "typ": "JWT"
}

 

followed by the payload which is the following JSON object:

{ 
    "exp": 1549989775, 
    "user": "alice", 
    "is_admin": false 
}

 

This token describes a JWT protected with an HMAC using SHA-256, with expiration date of 2019-02-12, for the user alice. The token also seems to deny administrative privileges. The third part, the signature, is created by signing the payload using a secret key in the HMAC calculation.

Removing the signature

The first thing to note is that the signature is made over the payload. Thus, the header is not protected. The header is still needed to describe how the signature should be calculated, i.e. which algorithm should be used in the verification phase. However, the malleability of the header caused problems in several previous versions of JWT libraries.

One algorithm defined in the JWT standard is the none algorithm. The purpose of this algorithm is to describe tokens for which verification is unnecessary (presumably since the verification has already been done on another level). However, consider a token like this:

{ 
    "alg": "none", 
    "typ": "JWT" 
}

with payload

{ 
    "exp": 1549989775, 
    "user": "alice", 
    "is_admin": true 
}

Note that the alg field in the header has been changed to none, and that the is_admin field has been changed to true. Now, if this token (after being encoded) is submitted to a flawed JWT implementation, using an empty signature field, its signature will be valid because of the none-algorithm! This allows an attacker to freely modify the payload, and as in this example grant themselves administrative privileges. An example of a CVE for this vulnerability is CVE-2018-1000531. The mitigation in the general case is to disallow tokens with the none algorithm when a token is expected to be signed.

Move from RSA to HMAC

While the previous attack was fairly straightforward, there is another possible flaw. Another supported JWT algorithm is RS256. This means that an RSA signature is used instead of an HMAC. This allows everyone with access to the public key to verify the signature, as opposed to only those with knowledge of the shared secret for the HMAC. The public key can be publicly posted, since it can only be used for verification of the token. To actually sign the token, the private key is required, which should be kept secret at all times.

Consider the following token:

{ 
    "alg": "RS256", 
    "typ": "JWT" 
}

with payload

{ 
    "exp": 1549989775, 
    "user": "alice", 
    "is_admin": false 
}

and assume that an attacker knows the public key.

The attacker can now construct a new token, with a modified algorithm and payload:

{ 
    "alg": "HS256", 
    "typ": "JWT" 
}

with payload

{ 
    "exp": 1549989775, 
    "user": "alice", 
    "is_admin": true 
}

Note that the algorithm has been replaced with HS256, and that we again try to get admin rights.

A JWT implementation will now try to verify this token, but instead of verifying it using RSA, it may interpret the public key as an HMAC secret instead! The attacker can replace the signature part with the corresponding HMAC, using the known public key as HMAC secret. The verifier will then consider the token as valid. Examples of CVEs for this vulnerability are CVE-2015-9235 and CVE-2016-10555. The mitigation against this flaw is to explicitly specify which algorithms to allow when verifying a token.

Write A Comment