Resources

A Guide to JSON Web Tokens (JWTs)

Written by Michael Jepson | Feb 18, 2025 8:30:42 AM

Not too long ago, JSON Web Tokens (JWTs) were widely regarded as a go-to solution for authentication, praised for their security, scalability, and simplicity. However, today, the penetration testing team at CybaVerse—along with other security researchers—frequently uncovers high and critical vulnerabilities in their implementations.

The thing is automated scanners don’t typically pick up JWT misconfigurations with default settings. A thorough penetration test, however, can expose serious flaws that, if exploited, could undermine confidentiality, integrity, and availability. These three principles form the foundation of cyber security, and failing in any of them can lead to financial loss, reputational damage, or regulatory fines. 

JWTs are just one option for authentication and authorisation, both of which are equally essential to security. Authentication confirms who you are, while authorisation determines what you can access. JWTs often include claims that define a user’s roles, permissions, and access levels, but if these are not properly validated, attackers can modify them to escalate privileges or bypass restrictions altogether. 

Understanding the risks is the first step to securing JWTs effectively. The next section covers how attackers exploit them, followed by ways to strengthen their security.

They Work and Why They Matter

Before diving into exploits, it’s important to understand what JWTs are and how they work. As previously mentioned, JWTs are widely used for authentication and authorisation. They allow systems to verify a user’s identity and access level without requiring session data to be stored on the server. Unlike traditional session tokens, all the necessary data is stored on the client-side within the JWT itself. This makes them a popular choice for highly distributed websites, allowing users to interact seamlessly across multiple back-end servers.

Figure 1 decoded example of a JWT 

A JWT consists of three parts: the header, payload, and signature. These are Base64-encoded and joined together with dots, forming a compact token that looks like this: 

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkN5YmFWZXJzZSIsImFkbWluIjp0cnVlLCJpYXQiOjE3Mzg2MTEzOTksImV4cCI6MTczODYxNDk5OX0.aS3zuaxqSRln7x1w8yoDZh6ikgxok4iHykNORwNsrVY 

Let’s break it down. 

Header 

The header contains information about how the JWT is signed.

{ 

  "typ": "JWT", 

  "alg": "HS256" 

} 

The alg field specifies the signing algorithm, such as HS256 (HMAC SHA-256) or RS256 (RSA SHA-256). The typ field just indicates this is a JWT. 

Payload 

The payload holds the claims, which are details about the user and their permissions.

{ 

  "sub": "1234567890", 

  "name": "CybaVerse", 

  "admin": true, 

  "iat": 1738611399, 

  "exp": 1738614999 

} 

In this example:

  • sub is the user ID 
  • name is "CybaVerse" 
  • role is "admin", meaning the user has high-level access 
  • exp is the expiration time, stopping the token from being used indefinitely

Claims like sub, exp, and role are often used for authorisation, helping systems decide what a user is allowed to do. 

Signature 

The signature makes sure the JWT hasn’t been tampered with. If someone tries to change the payload, the signature will no longer match, and the JWT should be rejected.

Next, we will look at how JWTs can be exploited and what can be done to secure them. 

Common JWT Security Exploits

Before getting into some of the documented common exploits, the most frequent issue found is authorisation. While this is not a direct flaw of JWTs, it is often a misconfiguration. Many applications have multiple user levels, yet access control is either poorly implemented or inconsistently applied across different areas. 

Just because an /api/admin endpoint is not visible on the web application, does not mean attackers won’t find it. Endpoint discovery is straightforward, and without proper access control, critical actions like creating an admin user can often be performed by unauthorised accounts. 

Even during black-box testing (meaning no information provided) with a self-registered account or even with no authentication, privilege escalation can be surprisingly straightforward, especially when verbose error messages from the server give away key information. An example is when we fuzz the correct endpoint, and find something like the below: 

https://webapp.com/api/users/create  

The server returns a 200 OKAY and then guides us through the process, first requesting an email, then a password, and eventually requesting a role parameter. With the right input, it is not long before privilege escalation is achieved. 

Issues like this are all too common, especially during a company’s first penetration test. While the list of JWT-specific vulnerabilities below is important, misconfigured authorisation remains one of the most frequent real-world security gaps.

1. Sensitive Information Disclosure 

JWTs are Base64-encoded, but they are not encrypted. A common mistake is storing sensitive information in the payload. Things like user IDs, email addresses, API keys or even plaintext passwords. 

How Attackers Exploit It: 

1. Capture a JWT from a request using a proxy like Burp Suite. 

2. Decode it with echo <token> | base64 -d or use jwt.io. 

3. Extract sensitive data and use it to escalate privileges or impersonate users. 

Mitigation: 

  1. Never store sensitive data inside a JWT payload.

  2. Encrypt sensitive data if absolutely necessary.

Figure 2 example of a password in JWT which can be seen after Base64 Decoding

2. Signature Validation Bypass

JWTs use a signature to verify that the token hasn’t been altered. If a server does not properly check the signature, attackers can modify claims and still gain access. In some cases, misconfigured systems even allow the none algorithm, meaning no signature is required at all. 

How Attackers Exploit It: 

1. Capture a valid JWT. 

2. Modify the payload, for example, changing "admin": false to "admin": true. 

3. Change the header to specify "alg": "none", removing the need for a signature:

{ 

  "alg": "none", 

  "typ": "JWT" 

} 

1. Remove the signature entirely or replace it with random data.

2. If the server accepts the modified token without verifying the signature, unauthorised access is granted. 

Mitigation: 

  1. Always validate the signature before processing a JWT. 

  2. Use secure JWT libraries that enforce signature verification and reject unsigned tokens.

  3. Ensure the server does not allow alg: none unless explicitly required for a specific use case.

3. Brute-Forcing Weak Signing Keys

If JWTs are signed with a symmetric algorithm for example HS256 they can be signed with weak keys (like password123) and these are easy targets for brute-force attacks. If an attacker can guess the key, they can sign their own tokens and gain access. 

How Attackers Exploit It: 

1. Capture a JWT. 

2. Use a brute-force tool like Hashcat:

hashcat -m 16500 -a 0 jwt_hash.txt wordlist.txt 

3. Generate valid JWTs with escalated privileges.

Mitigation: 

  1. Use long, randomly generated signing keys. 

  2. Never use hardcoded or default keys.

Figure 3 if the secret is very weak/common it can automatically be detected with the right configuration 

4. JWT Authentication Bypass via kid Header Path Traversal

The kid (key ID) header is used by servers to identify which key should be used to verify the JWT signature. However, if improperly implemented, it can be abused to reference arbitrary files on the server, potentially exposing sensitive information or allowing attackers to bypass authentication. 

How to Spot:

  1. Check if the kid value is directly used to reference keys on the file system.

  2. Test for directory traversal by injecting values such as: 

    ../../../../var/www/html/config.json 

    ../../../../dev/null

  3. Observe whether the server attempts to retrieve a file from an unintended location. You may even get content back but more likely you will get indications it’s trying to read the key. So, the next steps are.

Bypassing Authentication with a Leaked Key 

If the application loads signing keys dynamically, an attacker can point kid to a stored key file, such as: 

  "alg": "HS256", 

  "typ": "JWT", 

  "kid": "../../../../dev/null" 

If successful, the server will use the attacker-specified key, allowing them to forge a valid JWT and gain unauthorised access. 

  1. Alternatively misconfigured applications may allow file uploads and later reference them as signing keys. If an attacker can upload their own key and specify it in the kid value, they gain full control over JWT verification.

Mitigation: 

  1. Validate the kid value against a predefined set of trusted key IDs. 

  2. Reject absolute or relative file paths and ensure kid values are not used directly in file operations. 

  3. Sanitise user input to prevent directory traversal attacks. 

  4. Use asymmetric encryption (RS256) instead of HMAC (HS256) to limit key-based manipulation. 

Figure 4 shows a modified kid that was signed as NULL (00), which allowed us to sign our own JWT as an administrator and access the admin page shown to delete users

5. JWT Algorithm Confusion Attack 

JWTs allow different signing algorithms, and if the server isn’t strict, attackers can change the algorithm to bypass verification. 

How Attackers Exploit It: 

1. Obtain the application’s public key, for example, from /jwks.json or .well-known/jwks.json. 

2. Modify the JWT header to "alg": "HS256". 

3. Sign the JWT using the public key as the secret. 

4. If the server allows switching from RS256 to HS256, it treats the public key as an HMAC secret and validates the token. 

Mitigation: 

  1. Enforce strict algorithm validation and never allow switching from RS256 to HS256. 

  2. Prevent clients from modifying JWT headers. 

Figure 5 shows an example jwks publicly accessible that could be used in the attack detailed above 

These are just some of the documented attacks; there are many more, but hopefully, this provides insight into known JWT exploits. 

Considerations To Lock JWTs Down

1. Use Strong Signing Methods
  1. It is generally better to use asymmetric signing (RS256 for example) instead of HS256.

  2. Ensure the server does not accept any variant of none, NONE, None as an algorithm. 

2. Enforce Proper Authorisation
  1. Authorisation claims should not be editable.

  2. Validate scopes, roles and permissions properly.

  3. Ensure critical claims like admin, role, or user_id cannot be modified by users.

  4. Ideally don’t allow anything to be modified!

3. Implement Short Expiry & Rotation
  1. Keep access tokens short-lived.

  2. Use refresh tokens to generate new access tokens.

  3. Implement token revocation mechanisms like denylists.

4. Secure Storage 
  1. JWTs in localStorage or sessionStorage, could be vulnerable to XSS attacks.

  2. Use HttpOnly, Secure, SameSite cookies instead.

5. Prevent JWKS & Header Injection Attacks
  1. Hardcode trusted JWKS URLs and do not allow dynamic configuration.

  2. Validate kid (key ID) and jku (JWK Set URL) parameters against an allowlist.

  3. Sanitise user input to prevent path traversal in JWT headers. 

6. Monitor & Log Suspicious Activity 
  1. Log failed JWT validation attempts.

  2. Detect unusual login patterns (e.g., multiple logins from different locations).

  3. Rate limit authentication requests to prevent brute-force attacks. 

Final Thoughts 

JWTs are powerful, but they must be used securely. It is not uncommon for developers to assume JWTs are “secure by design,” but as we’ve seen, poor implementations lead to serious vulnerabilities. 

Automated scanners won’t find these issues out-of-the-box, which is why manual penetration testing is essential. If you’re building or testing an application using JWTs, take the time to harden your implementation, before someone else finds the holes for you. 

At CybaVerse, our award-winning penetration testing services go beyond automated scans, uncovering critical vulnerabilities before attackers do. Get in touch to see how we can help secure your applications.