# Testing JSON Web Tokens

|ID          |
|------------|
|WSTG-SESS-10|

## Summary

JSON Web Tokens (JWTs) are cryptographically signed JSON tokens, intended to share claims between systems. They are frequently used as authentication or session tokens, particularly on REST APIs.

JWTs are a common source of vulnerabilities, both in how they are in implemented in applications, and in the underlying libraries. As they are used for authentication, a vulnerability can easily result in a complete compromise of the application.

## Test Objectives

- Determine whether the JWTs expose sensitive information.
- Determine whether the JWTs can be tampered with or modified.

## How to Test

### Overview

JWTs are made up of three components:

- The header
- The payload (or body)
- The signature

Each component is base64 encoded, and they are separated by periods (`.`). Note that the base64 encoding used in a JWT strips out the equals signs (`=`), so you may need to add these back in to decode the sections.

### Analyse the Contents

#### Header

The header defines the type of token (typically `JWT`), and the algorithm used for the signature. An example decoded header is shown below:

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

There are three main types of algorithms that are used to calculate the signatures:

| Algorithm | Description |
|-----------|-------------|
| HSxxx | HMAC using a secret key and SHA-xxx. |
| RSxxx and PSxxx | Public key signature using RSA. |
| ESxxx | Public key signature using ECDSA. |

There are also a wide range of [other algorithms](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) which may be used for encrypted tokens (JWEs), although these are less common.

#### Payload

The payload of the JWT contains the actual data. An example payload is shown below:

```json
{
  "username": "administrator",
  "is_admin": true,
  "iat":[PII_REDACTED],
  "exp":[PII_REDACTED]
}
```

The payload is it not usually encrypted, so review it to determine whether there is any sensitive of potentially inappropriate data included within it.

This JWT includes the username and administrative status of the user, as well as two standard claims (`iat` and `exp`). These claims are defined in [RFC 5719](https://tools.ietf.org/html/rfc7519#section-4.1), a brief summary of them is given in the table below:

| Claim | Full Name | Description |
|-------|-----------|-------------|
| `iss` | Issuer | The identity of the party who issued the token. |
| `iat` | Issued At | The Unix timestamp of when the token was issued. |
| `nbf` | Not Before | The Unix timestamp of earliest date that the token can be used. |
| `exp` | Expires | The Unix timestamp of when the token expires. |

#### Signature

The signature is calculated using the algorithm defined in the JWT header, and then base64 encoded and appended to the token. Modifying any part of the JWT should cause the signature to be invalid, and the token to be rejected by the server.

### Review Usage

As well as being cryptographically secure itself, the JWT also needs to be stored and sent in a secure manner. This should include checks that:

- It is always [sent over encrypted (HTTPS) connections](../09-Testing_for_Weak_Cryptography/03-Testing_for_Sensitive_Information_Sent_via_Unencrypted_Channels.md).
- If it is stored in a cookie, then it should be [marked with appropriate attributes](../06-Session_Management_Testing/02-Testing_for_Cookies_Attributes.md).

The validity of the JWT should also be reviewed, based on the `iat`, `nbf` and `exp` claims, to determine that:

- The JWT has a reasonable lifespan for the application.
- Expired tokens are rejected by the application.

### Signature Verification

One of the most serious vulnerabilities encountered with JWTs is when the application fails to validate that the signature is correct. This usually occurs when a developer uses a function such as the Node.js `jwt.decode()` function, which simply decodes the body of the JWT, rather than `jwt.verify()`, which verifies the signature before decoding the JWT.

This can be easily tested for by modifying the body of the JWT without changing anything in the header or signature, submitting it in a request to see if the application accepts it.

#### The None Algorithm

As well as the public key and HMAC-based algorithms, the JWT specification also defines a signature algorithm called `none`. As the name suggests, this means that there is no signature for the JWT, allowing it to be modified.

This can be tested by modifying the signature algorithm (`alg`) in the JWT header to `none`, as shown in the example below:

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

The header and payload are then re-encoded with base64, and the signature is removed (leaving the trailing period). Using the header above, and the payload listed in the [payload](#payload) section, this would give the following JWT:

```txt
eyJhbGciOiAibm9uZSIsICJ0eXAiOiAiSldUIn0K.eyJ1c2VybmFtZSI6ImFkbWluaW5pc3RyYXRvciIsImlzX2FkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjI0MjYyMn0.
```

Some implementations try and avoid this by explicitly blocking the use of the `none` algorithm. If this is done in a case-insensitive way, it may be possible to bypass by specifying an algorithm such as `NoNe`.

#### ECDSA "Psychic Signatures"

A vulnerability was identified in Java version 15 to 18 where they did not correctly validate ECDSA signatures in some circumstances ([CVE-2022-21449](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/), known as "psychic signatures"). If one of these vulnerable versions is used to parse a JWT using the `ES256` algorithm, this can be used to completely bypass the signature verification by tampering the body and then replacing the signature with the following value:

```txt
MAYCAQACAQA
```

Resulting in a JWT which looks something like this:

```txt
[CREDENTIAL_REDACTED]
```

### Weak HMAC Keys

If the JWT is signed using a HMAC-based algorithm (such as HS256), the security of the signature is entirely reliant on the strength of the secret key used in the HMAC.

If the application is using off-the-shelf or open source software, the first step should be go investigate the code, and see whether there is default HMAC signing key that is used.

If there isn't a default, then it may be possible to crack guess or brute-force they key. The simplest way to do this is to use the [crackjwt.py](https://github.com/Sjord/jwtcrack) script, which simply requires the JWT and a dictionary file.

A more powerful option is to convert the JWT into a format that can be used by [John the Ripper](https://github.com/openwall/john) using the [jwt2john.py](https://github.com/Sjord/jwtcrack/blob/master/jwt2john.py) script. John can then be used to carry out much more advanced attacks against the key.

If the JWT is large, it may exceed the maximum size supported by John. This ca