Analyzing Broken User Authentication Threats to JSON Web Tokens
Executive summary
Akamai researchers have analyzed JSON web tokens (JWTs) as a vector for broken user authentication attacks, which is in the Open Web Application Security Project (OWASP) API Security Top 10, and uncovered different scenarios in which JWT threats and trends occur.
JWTs are responsible for securing APIs by issuing tokens (usually between clients and servers) to securely verify users. These tokens are one of the most common verification formats used and they contain information to be shared in the form of JSON objects.
Although each token isn’t encrypted, it is encoded and has a verification signature. We examined some of the many threats to JWTs that are due to improper implementation or low-entropy and short secret keys.
Account takeover, privilege escalation, and data leaks are three big potential risks that JWT users and organizations face when choosing a JWT as the token of choice in an API. We observed in our traffic that most of the JWTs use a symmetric algorithm, even though it’s theoretically less secure.
In this post, we offer best practices on how to handle these JWT threats.
Introduction
JWTs are a widely used, relatively easy to enact, token schema that hides many risks. These risks must be considered when implementing and securing JWTs. One of the most prominent risks to JWTs, which appears on the OWASP API Security Top 10, is broken user authentication.
Broken user authentication occurs when an API does not properly verify the credentials and identity of the user who is making the request. Unfortunately, this is a common vulnerability observed for JWTs and can lead to an attacker impersonating or accessing another user’s account, allowing privilege escalation and data leaks.
Protecting the pipeline
A JWT is a commonly used authentication token in APIs and web applications. We observed in our traffic that web applications are adopting more APIs and exposing them to the customer, which raises the need for seamless authentication in different API endpoints.
The API is the main pipeline of an organization, holding the authentication and authorization mechanisms, querying databases, and exposing most of the functions of the organization. For that reason, JWT threats are more dangerous in the API field. In this blog post, we will present the basics of JWTs, six threats to JWTs, and how to address those threats.
The basics of JWTs
JWT is a format for sending signed JSON data to transmit data in web applications, mobile applications, and APIs, mostly used for authentication only. The JWT structure consists of three elements: the header (also known as a JOSE header), the payload, and the signature, all separated by a dot (Figure 1).
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJpYXQiOjE2NzYyMTc5NTAsImV4cCI6MTcwNzc1Mzk1MCwiYXVkIjoiYWthbWFpLWJsb2ciLCJzdWIiOiIiLCJjb21wYW55IjoiQWthbWFpIiwidXNlciI6IkFrYW1haS1yZWFkZXIiLCJhZG1pbiI6Im5vIn0.kMPz3Z7BSlBTJKijD8bcrpzTZejX7VCZ77w5oQwJO6I
Fig. 1: This is an example of the three elements of the JWT structure
The header and payload
Both the header and payload are Base64, URL-safe, encoded, IETF-specified recommendations for fields (Figure 2). “Encoded” means that they are not directly readable by the human eye but are easily decoded by the server. The purpose of this is to ensure the integrity and usability of the data, while keeping it “URL safe.” This way, it won’t have overlapping characters that are used by web browsers.
The JOSE header is used for both JWT and JSON web encryption (JWE), which is basically an encrypted JWT that helps to avoid most common JWT attacks. The field that specifies the chosen token method is “typ” (typ:JWT/JWE). The payload may contain registered fields (claimed by the Internet Assigned Numbers Authority) or custom fields, depending on the implementation.
Fig. 2: Encoded JWT
The signature
The purpose of the signature is to verify the token — meaning that the server forged the token and the data hasn't changed since the token was created.
There are two main steps to creating the signature:
Applying an encryption algorithm (e.g., MAC) on the header and payload, separated by a dot (the algorithm is using a secret key)
Applying a hashing algorithm (e.g., SHA256) on the encrypted header and payload
When a JWT is used, the server verifies the signature in order to validate the token itself. (Later in this post, I explain the different algorithms that are used for validation.)
JWTs are widely employed for authentication in web applications because of their ease of use (including in large scale) and ease of implementation, and because the server’s required data is saved on client side. But there are risks associated with JWTs even though they are signed. JWT uses plain text, and every implementation consists of different payload fields. So, there is a wide attack surface and plenty of room for errors.
By exploring some of the more common and interesting threats that JWTs face, we can be more aware of vulnerabilities, more easily detect malicious behavior, better mitigate risks, and prepare necessary security measures to safeguard against potential exploits.
It is our goal to provide users with a more secure approach in working with JWTs and to provide security professionals and administrators with the tools and recommendations necessary to help fulfill their due diligence.
Six threats to JWTs
1. Allowing the server to use a token without validation
Because a JWT is signed instead of encrypted, validation has to be done before any use. In the most basic threat scenario, in which an application doesn’t validate at all, an attacker can edit the payload (e.g., privilege escalation) and keep the signature untouched, or even delete it and get higher permissions.
Another method is using the “alg” parameter in the header, which represents the algorithm that has been used to sign the token. “None” is a legitimate value (and the JWT is named unsecured JWT), so anyone can easily sign the token. At the back end, there is a possible implementation of JWT verification as well.
Begin this approach by looking for the “alg” field in the header. After that, use the specified algorithm to verify the token. The “None” algorithm represents no need for signature and verification for that token. Editing the token’s payload, header (to alg:none) and “self-signing” (deleting the signature) will lead to a successful attack.
Example
Figure 3 shows an example of a decoded JWT.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZXhhbXBsZSBuYW1lIiwiaWF0Ij
oxNjU5MjM5MDIyfQ.PrhAp2DUWXoL01_odyLATuzrwn5rMtY1IVsP8y4LH5E
decoded:
{
"alg": "HS256",
"typ": "JWT"
}
.{
"name": "example name",
"iat": 1659239022
}
Fig. 3: Decoded JWT example
We assume that the API endpoint uses the “alg” field in the payload as the chosen algorithm for verification.
But we would like to edit the JWT (Figure 4). In this way, we succeed in forging a valid JWT of another’s user.
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJuYW1lIjoiZXhhbXBsZSBuYW1lIiwiaWF0Ijo
xNjU5MjM5MDIyfQ.
decoded:
{
"alg": "none",
"typ": "JWT"
}
.{
"name": "other person's name",
"iat": 1659239022
}
Fig. 4: JWT edited to exploit alg:none.
Best practice: This one is simple: Always validate by using a hard-coded predefined algorithm before using the token. Do not use the alg parameter as a pointer for the verification algorithm.
2. Using the same private key for different applications
There are many companies that choose to have separate registration for different applications. Using the same private key will create a valid token in both applications, but with a different user ID. In that case, it’s a matter of making a request to a second application with the first’s application token, and taking over an account (not of your choice) (Figure 5).
Best practice: This is another simple rule of thumb: When separating user IDs, use different private keys.
3. Using a weak signing algorithm
There are two main families of algorithms: symmetric (HSXXX) and asymmetric (RSXXX,ESXXX,PSXXX). A symmetric algorithm requires one shared secret; an asymmetric algorithm requires private and public keys (Table 1).
Symmetric |
|||
---|---|---|---|
HSXXX |
HMAC + SHA |
Relatively weak algorithm |
Uses shared secret |
Asymmetric |
|||
RSXXX |
RSA + SHA |
More secure algorithm |
Uses private and public keys |
ESXXX |
ECDSA + SHA |
Most secure algorithm of the three |
Uses private and public keys |
Table 1: The two main families of algorithms
From the attacker's perspective, a symmetric algorithm is easier to brute force, there is no need to collect public keys, and compute time is as efficient as possible. We observed in our traffic that symmetric algorithms are more widely used than asymmetric algorithms (Figure 6).
Theoretically, symmetric algorithms require less computation, so in large scale they might have better performance than asymmetric algorithms.
Best practice: Use asymmetric algorithms. We recommend using the ECDSA algorithm, which is currently the safest choice (cryptographically). A properly secured symmetric key is acceptable if the secret is high entropy and sufficiently long.
4. Choosing a short and/or low-entropy private key
A short and/or low-entropy private key could be brute forced quickly and at low cost. There is easy-to-use cracking software that can be installed on high-performance cloud environments with graphics processing units (GPU). GPU performance is growing rapidly and it has an effect on cracking capabilities. Today’s high-end graphics cards are cracking almost 2.5 times faster than three years ago.
A comparison of how many days it would take to crack an eight-character private key (regardless of its entropy) three years ago and today is shown in Table 2.
High-end graphics card |
Days to crack |
---|---|
Three years ago |
10.5 days |
Today |
4.5 days |
Table 2: The number of days it would take to crack an eight-character private key
A seven-character private key that would have taken about 24 hours to crack a few years ago can be cracked in less than 90 minutes. The uniqueness of this attack is that it runs offline, without any chance of defense from the organization side, or even any knowledge that you are under such kind of attack.
Imagine yourself as a state-sponsored attacker who would create an orchestrated system for that. Holding the JWT’s private key means the attacker is able to forge a signed token and impersonate any other user just by knowing its ID.
Best practice: Choose a long (at least 10 characters) and high-entropy private key. We recommend using RSA as a secret generator.
5. Keeping sensitive data in a JWT’s payload
JWTs are Base64 URL-encoded so the payload isn’t encrypted. Storing DB names, incremental IDs (of all kinds), or any server internal fields and data, creates excessive data exposure, which may help an attacker in other attack vectors (e.g., broken object level authorization).
Best practice: Do not store sensitive data in a JWT. If you need to store sensitive data, use JWE.
6. Confusing the keys
With asymmetric algorithms, a private key is used to forge the JWT (the signature part) and a public key is used to validate the JWT. An attacker can change the algorithm specified in the header to a symmetric one (e.g., HS256) and create a signature using the public key that is legitimately used by the server for verification.
In that scenario, the not-properly-implemented back end will use the public key and run the symmetric algorithm (as the attacker specified in the header) to successfully verify the JWT that was forged by the attacker.
Example
Figure 7 shows the server’s process of forging a new JWT and Figure 8 illustrates the server’s JWT verification process.
In the last part of verification, a throw error in the comparison can happen for one of two reasons (or both):
The header and payload were edited, but the signature wasn’t
There is a misconfiguration of the right algorithm or the right key
Figure 9 shows a key confusion attack.
The final step: The server will verify the token with the process illustrated in Figure 8, in which the server trusts the alg field. It will verify the token by running the HS256 algorithm with the public key as a secret, which is exactly what the attacker planned.
Best practice: Use predefined algorithms in the verification process and do not trust user input.
Conclusion
JWT is the most common authentication token. It enables the use of many web applications and APIs while being authenticated, without frequently signing in. However, there are many threats that come with JWTs because they are neither encrypted nor implemented with security in mind.
Computer power is evolving rapidly, which puts weak secret keys at risk and can break them in days instead of years. In the case of JWTs, this kind of attack may run offline without an organization’s knowledge of the attacks.
Symmetric algorithms are the most common type of algorithm, which just makes attackers’ lives easier.
More complex threats, such as authentication attacks, also exist — but it is difficult for attackers to automate authentication attacks. Simple JWT attack vectors, however, can be automated and massively exploited.
For these reasons (and many others), it is crucial that users are aware of the vulnerabilities of JWTs and the best security practices to implement to safeguard against potential exploits. We hope that this analysis serves as a handy guide in protecting JWTs from broken user authentication, one of the OWASP API Security Top 10 leading threats.