JSON Web Tokens, or JWTs, are an encoded set of claims commonly seen in REST APIs and Single page web applications (SPAs). These encoded claims are used to provide identification of the requester and other information related to accessing. It is a stateless mechanism, and the token is sent with every request requiring identification, typically in the Authorization header or as a URL parameter. Although there is some debate about them as session tokens (https://redis.com/blog/json-web-tokens-jwt-are-dangerous-for-user-sessions/) they are still used in that capacity as well. JWTs must be designed and implemented securely, or issues will be present. Qualys Web Application Scanning (WAS) will now detect vulnerabilities related to algorithms used in JWTs.
JWT Structure
A JWT contains three parts:
Header – Section contains the type of token and the algorithm in use. The most common algorithms seen are HMAC with SHA-256 (HS256), a symmetric algorithm, and RSA with SHA-256, an asymmetric algorithm. May include additional fields such as Key ID (kid).
Payload: Section contains the information about the user referred to as Claims.
Signature – Section is used to validate the token. An operation is performed on two previous parts with the addition of a secret or private key.
Each of these sections is combined into a single token using the following:
token = base64urlEncoding(header) + ‘.’ + base64urlEncoding(payload) + ‘.’ + base64urlEncoding(signature)
As the portions of the token are base64 encoded, it is trivial to decode and view the contents. Let’s look at an example taken from the Tiredful API (https://github.com/payatu/Tiredful-API):
Encoded:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJncm91cCI6IlN1cGVyaGVyb2VzIiwidXNlciI6ImJhdG1hbiIsImV4cCI6MTY2MjY1NDAwNiwiZW1haWwiOiJiYXRtYW5AZ290aGFtLmNvbSJ9.ROm-7vwGjTn8aaVOEodtS9u683-LJ6m8NdRAuAmiIik
Decoded:
Header
{
“alg”: “HS256”,
“typ”: “JWT”
}
Payload
{
“group”: “Superheroes”,
“user”: “batman”,
“exp”: 1662654006,
“email”: “[email protected]gotham.com“
}
Implementation Issues
Qualys WAS is introducing two new QIDs to detect algorithms in JWTs found in Authorization headers. The QIDs are passive checks that will decode the token and review the contents.
QID 150571: JWT in Authorization Header Uses “none” Algorithm.
The QID will detect tokens using the “none” algorithm. This is the weakest “algorithm” as there isn’t a signature to validate the token. The lack of a signature will allow an attacker to easily modify a token to set arbitrary claims such as elevating privileges or impersonating another user.
{“alg”: “none”, “typ”: “JWT”}
QID 150572: JWT in Authorization Header Uses Symmetric Algorithm.
The QID will detect tokens using the HS256 algorithm. When designing the JWT implementation, one must select an algorithm to use. Choosing a symmetric algorithm is weaker as anyone knowing the secret can create or validate a token. If the secret is known, an attacker can forge a token with an arbitrary set of claims.
{“alg”: “HS256”, “typ”: “JWT”}
Other considerations
Algorithm Confusion
If a system designed for an asymmetric algorithm can be forced to process a JWT as HMAC symmetric algorithm or the none algorithm, a valid JWT could easily be forged. Ensure a whitelist of approved algorithms is utilized to prevent unexpected algorithms from being accepted.
Sensitive Content
As JWTs are base64 encoded, they can easily be decoded. It will be disclosed if sensitive data is stored in the payload portion. Do not use the payload section to store any data that should not be publicly available.
Secret Disclosure
In the case of symmetric or HMAC usage, a shared key/secret is used to create and verify the token. Implementation have been observed where the secret key is contained in client-side code or is easily guessable. If this key is known, anyone can create and verify valid tokens.
For asymmetric (RSA and EDCSA), if the private key leaks, valid JWTs could be forged. Of course, if a private key ever leaks, it’s never good.
Key ID Attacks
The Key ID (kid) parameter, if used, contains reference to the key utilized. If arbitrary values are accepted, this can lead to attacks such as directory traversal, server-side request forgery, and others.