Insights

The Dangers of Using JSON Web Tokens for Session Management 

Author:

Richard Mason

As penetration testers, there are several issues we find time and time again in relation to the security of an application’s, or API’s, session management. In this insight we’ll look at session management using JSON Web Tokens (JWT) and outline some of the most common security issues we find during testing. 

But before we do, what is session management and what is JSON web token (JWT)? 

Session management & JSON web tokens (JWT) 


Session management refers to the process used by web applications and APIs to maintain a user’s state during their interaction with the application/API. There are multiple ways in which a web application/API can manage sessions but, in this insight, we are going to be looking at JSON Web Tokens (JWT). Most commonly used by API’s, but also commonly seen to be used by web applications.
 

JWT is an open standard (RFC 7519) defining a self-contained way to transmit information between parties as a JSON object. JWTs are often used for Authentication and Information Exchange workflows as JWTs provide a portable method of exchanging data with a small overhead that can be used between systems of different domains. These tokens are stateless, meaning the server doesn’t need to keep track of them. Instead, all the information needed for authentication and authorisation is contained within the token itself. 

Breaking down a JWT  

A JWT consists of three parts; the header, payload and signature, which are separated by a single ‘.’ The below is an example of a simple JWT:

				
					eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.NwbiFB4i37vWuTOpyf3hZikNuQozm7q5BeVO1hRpRzM 
				
			

The above probably looks random but the token is base64url-encoded and if we decode the token, we can see what information is contained within each component. The table below contains the plaintext of each component. 

Breaking down a JWT - Pentest Limited

Header 

The header will commonly consist of two parts: the type of the token, which is JWT, and the signing algorithm. The signing algorithm can either be symmetric or asymmetric: 

  • Single secret key using the HMAC algorithm (such as HS256), 
  • Public/private key pair using RSA (such as RSA256).

Payload 

The payload contains the tokens’ claims. Claims are statements about an entity (e.g. the user) and additional data. There are three types of claims: registered, public, and private. 

  • Registered Claims – These are a set of predefined claims. They are not mandatory but recommended. Common registered claims that we see are iss (issuer) and exp (expiration time). 
  • Public Claims – Public claims are custom claims that are intended to be shared across different systems and are registered with IANA’s JSON Web Token Claims Registry or follow a naming convention that avoid collisions. These claims provide information that can be universally understood, such as a user’s email. 
  • Private Claims – Private claims are custom claims defined for use between the JWT issuer and a specific application. These claims provide more specific information regarding the user and the application such as the user’s permissions. They are not subject to naming conventions, which allows for full flexibility.

Signature 

The signature is used to ensure the integrity of the token and is created by taking the encoded header, the encoded payload, a secret, the algorithm specified in the header and is signed using that. 

Issues with JWT  


Now time for the part of the insight that you are probably most interested in…the issues we most commonly see during pen testing. As mentioned, these are issues we commonly see, not an exhaustive list.
 

Sensitive data contained within JWT/JWE not used  

As we have seen previously, we have been able to decode the JWT and review the contents of the token. Assuming the JWT is correctly configured with a strong secret, only the integrity of a token is considered secure and not the confidentiality. Let’s look at the below JWT. 

				
					eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwidXNlcm5hbWUiOiJqZG9lIiwicGFzc3dvcmQiOiJQYXNzd29yZDEyMyEifQ.lmqTD39EaALpNK4U3rF0je4foyg38aMJy-HP9n66jt4 
				
			

The decoded payload of the above token contains something we probably don’t want to be included. 

				
					{ 
  "sub": "1234567890", 
  "name": "John Doe", 
  "username": "jdoe", 
  "password":"Password123!" 
} 
				
			

As you have probably guessed there is a claim containing the user’s password. Hopefully you would think that containing the password within the token isn’t the smartest decision, however passwords, their corresponding hashes and other sensitive data are often found.  

Logout does not invalidate session 

One of the most common issues we see with the use of JWTs is that after a user initiates a logout the JWT remains active. As consultants we can replay requests after we have logged out of an application and the application returns content as if we were still logged in. This issue is inherent with JWTs as they cannot be revoked in the same way a traditional cookie can and will remain valid until they expire. In this scenario, tokens should be set with an appropriate expiry time. E.g. if sensitive data is accessible then a short expiry time should be implemented. Additionally, a hashmap of “forcibly expired” tokens should be implemented which will tell the application server that the token is no longer valid. 

Signature not validated  

Another issue we commonly see that can have a drastic impact to the security of applications and APIs is when the token’s signature component is not validated. The below is an example of a JWT where the user’s permissions are contained within the JWT’s payload. 

				
					eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOmZhbHNlfQ.NwbiFB4i37vWuTOpyf3hZikNuQozm7q5BeVO1hRpRzM 
				
			

If we decode the payload of the above JWT token, we can see the below.  

				
					{ 
  "sub": "1234567890", 
  "name": "John Doe", 
  "admin": false 
} 
				
			

As shown the token contains a claim called admin. When a token’s signature is not correctly validated the claims within the payload can be modified and will be accepted by the server. In this example, although simple, would allow and attacker to elevate their permissions by modifying the admin claim to true as we have done below

				
					{ 
  "sub": "1234567890", 
  "name": "John Doe", 
  "admin": true 
} 
				
			

The below token has the modified payload but as we can see the signature has not been changed. 

				
					eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOmZhbHNlfQ.NwbiFB4i37vWuTOpyf3hZikNuQozm7q5BeVO1hRpRzM 
				
			

Weak secret used allowing for brute force attacks  

The use of a symmetric signing algorithm, meaning a single secret key is used to generate and validate the JWTs signature, leaves the token’s secret key susceptible to brute force attacks.  

Realistically for this attack to succeed the secret used to sign the token has to be weak and/or easily guessable. If an attacker can determine the secret this would result in them being able to create their own token to be used against the application/API. 

The token below has been generated with a secret that is contained within the Rockyou.txt wordlist (which is a list cultivated from known data breaches). Feel free to have a go at figuring it out.  

				
					eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.omdX47odTaJW8L9kEdfE3QpAWC3wHvn2FG1iFvKbdic 
				
			

Mitigating the issues 


Rest assured there are steps which can be undertaken in which JWTs can be secured:
 

  • Do not include sensitive data within JWTs 
  • Use asymmetric signature algorithm  
  • If a symmetric signing algorithm is required, ensure the secret key used to sign the JWTs is strong. 
  • Ensure JWT’s claims (such as exp) and signature is validated in every request 
  • Implement an appropriate expiry time 
  • Implement a token revocation mechanism such as token denylists 
  • Use Json Web Encryption (JWE) 

Put your session management security to the test 


This insight gives you a good general overview of the common security issues we find during testing and by implementing some of the mitigation steps outlined you can dramatically improve the security posture of your application when using JSON Web Tokens for session management purposes. However, every application is different, and the only way to truly know if it is secure is to put it to the test.
 

That’s exactly what our web application penetration testing service is designed to do, to help you uncover vulnerabilities and provide you with detailed remediation advice you need to mitigate the dangers. 

So, if you’re concerned about the security of your application’s session management or your applications wider security posture, why not give Pentest a call and see how we can provide you with the security assurances you need.  

Looking for more than just a test provider?

Get in touch with our team and find out how our tailored services can provide you with the cybersecurity confidence you need.