Understanding JSON Web Tokens (JWT)

Arpit Jain
Calvin Codes
Published in
7 min readAug 14, 2019

Introduction

The article starts off with a real-life problem statement and attempts to solve it using concepts similar to that of JWT. This is followed by an in-depth analysis of JWT, discussion of its various use-cases, and eventually some of its vulnerabilities.

Problem Statement (kinda real-life)

Alice, Bob, and Chuck are three friends. Alice writes a letter to Bob, which is delivered to Bob by Chuck. Alice and Bob are now concerned, “What if Chuck tampers the letter’s content on the way?”

Solution (kinda JWT)

So Alice and Bob decide that Alice will add a special word at the end of the letter. This special word will comprise of initials of all the words in letter’s body (example below). The logic of special word creation is kept a secret, known only to Alice & Bob and NOT to Chuck (more on this later).

Alice creates (Letter + Special_Word) combination

Say, Alice writes this letter,

Hi Bob, I will be able to attend the party.

Thus, the special word would be,

H.B.I.W.B.A.T.A.T.P
Explanation: Hi.Bob.I.Will.Be.Able.To.Attend.The.Party (initials of all words)

Alice then hands over the letter to Chuck which says,

Hi Bob, I will be able to attend the party.H.B.I.W.B.A.T.A.T.P

Case 1: Bob validates — Letter NOT tampered by Chuck

In this case, Chuck would deliver the following to Bob.

Given that Bob also knows how the special word is generated, he looks at the body of the letter and generates the expected special word using it. Bob then matches the expected special word with the actual special word added by Alice.

In this case, Bob generates expected special word as H.B.I.W.B.A.T.A.T.P. As this is same as the actual special word, Bob safely assumes that Chuck has not tampered the letter.

Case 2: Bob validates— Letter is tampered by Chuck

In this case, Chuck tampers the letter and delivers the following to Bob.

Again, Bob generates expected special word as H.B.I.W.N.B.A.T.A.T.P (N is extra as Chuck tampered the letter by adding the word “not”).

As the expected special word is not the same as the actual special word, Bob gets to know that Chuck has tampered the letter.

Assumptions (for the sake of simplicity in this example)

  1. Chuck only adds or removes one or multiple words while tampering.
  2. Chuck can not alter a word while tampering, like changing “can” to “can’t”
  3. Chuck does not know the special word creation logic and thus does not modify the special word while tampering (more on this later).

JSON Web Tokens (JWT)

As per RFC 7519, JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.

Simply put, JWT is a JSON string. The sender (Alice) of JWT adds a signature to it. The receiver (Bob) of JWT can verify the signature and thus trust JWT’s content.

How to create JWT?

JWT has the following structure (detailed explanation in subsequent sections)

header.payload.signature

Both header and payload are base64 URL encoded versions of actual header.json and payload.json objects (explained below)

header.json

Describes the type (typ) of the object, and the algorithm (alg) used for signature creation (detailed explanation in signature section).

payload.json

This is a custom object where the sender adds in all the information she wants to send. For example, Alice can have the following payload in her JWT.

Consider another example, where an application uses JWT for authentication. In this case, the payload can be user details.

Payload attributes (“id”, “loginSource”, “maxIdleTime”) are also called claims

Signature

Signature is the core of JWT. It is created using the following 5 steps process.

Step 1: Encode header.json object using base64 URL encoder

Step 2: Encode payload.json object using base64 URL encoder

Step 3: Concatenate encoded header and encoded payload using period (.) to create data

Step 4: Create hash of data using hash_alg (alg) specified in header.json and a secret key (more about secret key later)

Step 5: Encode hash using base64 URL encoder to create signature

Putting it all together

Create JWT by concatenating header (red text), (period), payload (purple text), (period), and signature (blue text).

header.paylod.signature (Source: https://jwt.io/)

What the heck is this “secret key”?

Before we answer that, let us quickly revise the fundamentals of hashing.

Hash functions map binary strings of an arbitrary length to small binary strings of a fixed length. A cryptographic hash function has the property that it is computationally infeasible to find two distinct inputs that hash to the same value. Small changes to the input data result in large, unpredictable changes in the hash.

hash = hash_alg(data, secret_key) // Step 4 of JWT creation

The basic idea here is to generate a cryptographic hash of the data combined with a shared secret key. The secret key is already known to both sender and receiver via some mechanism (which is beyond the scope of this article).

This resulting hash can be then used to verify the transmitted message for determining a level of trust, without transmitting the secret key (Struggling to comprehend this? Move on to the next section covering this in more detail.)

How JWT verification establishes trust?

Assumption: Secret key is already known to the receiver and is NOT transferred along with JWT.

JWT verification is reverse-engineered JWT creation. JWT is verified using the following 5 steps process.

Step 1: Extract header from received jwt

Step 2: Extract payload from received jwt

Step 3: Concatenate extracted header and extracted payload using period (.) to create received_data

Step 4: Create hash of received_data using hash_alg (alg) specified in the received_header, and the already known secret key

Step 5: Encode hash_of_rcvd_data using base64 URL encoder to create expected_signature

Step 6: Extract received_signature from received jwt and compare it with expected_signature

“secret_key” must be kept secret

If you are with me till now, it should be fairly easy to deduce that any tampering with the JWT will end up generating an expected_signature (much) different from the received_signature (thanks to the cryptographic strength of hash functions).

Let us play out how can an attacker successfully tamper a JWT.

Step 1: Attacker modifies the header or payload or both components of JWT
Step 2: Attacker extracts the hash algorithm (alg) attribute from header
Step 3: Attacker now has to someone know the secret_key used for sig creation
Step 4: Attacker modifies the signature component of JWT (code below)
Step 5: Attacker puts it all together and creates tampered JWT (code below)

tampered_sig = hash_algo(tampered_payload, secret_key)tampered_jwt 
= tampered_header + "." + tampered_payload + "." + tampered_sig

Thus, for a successful attack, the secret_key must be compromised.

Trusted & Verified! What next?

Once trusted and verified, the receiver is all set to use the claims provided in JWT payload.

Showtime | Using JWT for authorization

This is a good time to introduce a more technical example which uses JWT for authorization.

Consider a web service which has an authentication server and an application server. The user sign into the authentication server using a login mechanism (Username-Password, Google OAuth, Facebook Login, etc). Upon successful login, the authentication server uses a centrally stored secret key to create a JWT and returns this JWT to the user.

User gets JWT from authentication server

The user now sends a resource access request to the application server, along with this JWT. The application server uses the same centrally stored secret key to verify the JWT and accordingly allow or restrict user’s resource access.

User uses JWT to access resource via application server

JWT is all about encoding, NOT encryption

It is important to understand that given a JWT, anyone can use base64 URL decoder to decode and read its components.

JWT in itself is an encoded string, NOT encrypted.

Hence, it is recommended not to add any sensitive information in JWT header or payload unless JWT is transmitted over an encrypted network.

Possible use cases of JWT

Here are a few potential use cases of JWT. Note that JWT usage is much wider and not limited to these examples.

  1. Authorization (we saw this example): After successful login, subsequent requests from the user can include a JWT, which can be validated by the application server to authorize user’s access to various resources.
  2. Secure Information Exchange (Alice, Bob, and Chuck example): As JWT can be digitally signed, thus the receiver can validate that the sender of the JWT is who she claims to be.

JWT vulnerabilities

The following articles discuss some of the JWT vulnerabilities discovered in the past across various JWT libraries. I hope that most of them do not have these vulnerabilities now.

Note: In this article, I have used a symmetric-key algorithm (HS256) for hashing, which involves only one secret key. One can also use asymmetric algorithms like RS256, which involves public and private keys. The private key is used in JWT creation, and the public key is used in JWT verification.

Like what you read? From a quick cheer to a standing ovation, clap to show how much you enjoyed this story. Follow to get regular updates.

--

--

Arpit Jain
Calvin Codes

Scalability & Big Data Enthusiast | Microsoft | Sumo Logic | UW Madison | Myntra | IIT Guwahati