fix

How to Properly Secure Your JWTs

Explore best practices and techniques to protect your JWTs from common security vulnerabilities and keep your applications secure.

By
Omkar Hiremath
5 min read

In recent years, JSON Web Tokens (JWTs) have become a popular method for managing user authentication and authorization in web applications. JWTs provide a way to transmit information between parties in a self-contained format. However, as with any security mechanism, there are risks associated with using JWTs that must be properly addressed to ensure the security of your application.

In this post, we will explore the basics of JWTs, including what they are and how they differ from cookies. Then we’ll look into some challenges developers face with JWTs. Finally, we will outline some of the best practices for implementing JWTs securely. By the end of this post, you should have a better understanding of how to properly secure your JWTs and ensure the integrity of your web application.

What are JWTs?

JWT stands for JSON Web Token, it is an open standard format for securely transmitting information between parties as a JSON object. JWTs consist of three parts separated by a period:

  1. Header: describes the type of token and the algorithm used to sign it.
  2. Payload: contains the information that is being transmitted.
  3. Signature: used to verify the authenticity of the token.

Let’s take a look at the following example of a JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTY3ODk2NDY4MCwiZXhwIjoxNjc4OTY4MjgwfQ.tQC7xwGRKg-p4bojB2mS7A-26_wNLwlrTeiInKH6LLc

If we break this down into its parts, the first part (eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9) is the header and it decodes to:

{

“alg”: “HS256”,

“typ”: “JWT”

}

The second part (eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTY3ODk2NDY4MCwiZXhwIjoxNjc4OTY4MjgwfQ) is the information being transmitted and it decodes to:

{

 "sub": "1234567890",

 "name": "John Doe",

 "admin": true,

 "iat": 1678964680,

 "exp": 1678968280

}

“iat” is the time the token was issued and “exp” is when the token would expire. These are some of the standard fields you would find in JWTs.

The last part (tQC7xwGRKg-p4bojB2mS7A-26_wNLwlrTeiInKH6LLc) is the signature.

JWTs are often used in authentication systems to generate tokens that can be passed between different systems or services to authenticate the user and provide access to resources. JWTs are self-contained, which means that all the information needed to validate the token is contained within the token itself. This makes them easy to use and implement, as well as secure since they are signed and can be verified by the receiving party.

How are JWTs Different from Cookies?

Cookies are pieces of information that are stored on a user's computer by a web application typically used to store user preferences, session data, and other information. JWTs and cookies are both commonly used for client-side storage and transmission of data, but they are different in some ways.

Cookies
JWTs

Security

Can be easily manipulated without detection if not properly secured.

Digitally signed and can be validated on the server. Manipulation can be detected.

Size

Limited to 4KB.

Can contain much more data, up to 8KB.

Dependency

Often used for session data on the server-side. The server needs to store the session map.

Contains all the necessary information in the token. Doesn’t need to store data on the server.

Storage Location

Browser cookie jar.

Local storage or client-side cookie.

Where are JWTs Stored?

JWTs are typically stored on the client side, either in local storage or in a client-side cookie.

Local storage is a type of web storage that allows web applications to store key-value pairs in the browser, which can persist even after the browser is closed or the computer is turned off. JWTs can be stored in local storage using JavaScript code that sets or retrieves the token.

Client-side cookies are another way to store JWTs on the client side. To store a JWT in a cookie, the server can set a cookie with the token value and expiration date, and the client-side code can retrieve and send the cookie back to the server on subsequent requests.

Why do JWTs Suck?

While JWTs themselves are not inherently flawed, how they are implemented by developers can lead to security weaknesses in applications. There are a few common mistakes that developers can make when using JWTs. When you pair these mistakes with other vulnerabilities, they can cause significant damage to an application. For example:

  • Cross-site scripting (XSS): When JWTs are used in conjunction with XSS vulnerabilities, an attacker can potentially steal a user’s JWT and use it to gain unauthorized access to the application.
  • Authentication: If a signature exclusion vulnerability exists, an attacker can modify the contents of a JWT without invalidating the signature, allowing them to potentially gain access to sensitive information or perform unauthorized actions within the application.

Let’s look into some more challenges one would face while using JWTs.

Storage

The storage method chosen by developers can significantly impact the security of the JWTs and the application. Storing JWTs in memory is an effective approach because the data is only accessible within the browser's memory space. This means that if the browser is closed or reset, the JWT is lost. However, this approach may not be ideal for all applications.

On the other hand, session storage provides a more persistent storage solution for JWTs, as the data is stored within the user's session. However, session storage is still vulnerable to XSS attacks. An attacker can steal JWTs and gain unauthorized access to the application.

The best option is to store JWTs in a cookie with proper flags set, such as httponly, secure, and samesite. This is the only place where the JWT cannot be targeted by cross-site scripting (XSS) attacks.

Sensitive Data

Unlike cookies, which do not contain sensitive information, JWTs can contain a name and other sensitive information that should not be exposed to unauthorized parties. Developers need to be aware of what information they are storing in JWTs and ensure that it's information they can tolerate exposing to others.

JWTs are not meant to be modified, but they can be decoded to view the information inside them. Modifying a JWT is essentially an attack, and developers need to check if their application verifies the integrity of the encoded JWT beyond its initial issuing.

Credit card information, SINs, social security numbers, and other personally identifiable information should not be stored in JWTs. Although this is not as common anymore, it's still a good practice to follow through with education and awareness.

Invalidating JWTs

Developers don’t always remember to invalidate JWTs when users log out or change their passwords. This means that if a user logs out of an application, the JWT should become invalid, and if someone tries to use the same JWT, it should not work. But if developers do not invalidate the JWT, it can cause issues.

Imagine a library where a user logs into an app that uses JWTs. The next person comes in and uses the same computer without closing the browser. They can then go to the browser history, find the same site, and use the JWT key. This poses a risk of account takeover, especially in shared spaces.  

Sometimes changing your password does not invalidate the old JWTs. If your account has been compromised, you would hope that the hacker loses access, but without proper invalidating of JWTs, they can still have access to your account through the old JWTs. All-in-all invalidating JWTs is a crucial implementation when using JWTs.

While JWTs themselves are not inherently insecure, developers need to understand the potential risks and vulnerabilities associated with their use. By following best practices for securing JWTs, such as the refresh token feature, developers can minimize the risk of security issues arising in their applications. Let’s look at some best practices to follow when using JWTs.

Best Ways to Securely Implement JWTs

JSON Web Tokens (JWTs) can be a powerful tool when implemented securely. Here are some best practices for implementing JWTs:

  • Use strong algorithms like HMAC-SHA256 or RSA to sign and encrypt your tokens. Avoid using weak algorithms like HMAC-MD5.
  • Set an expiration time for the JWT to limit its validity period. This ensures that even if a JWT is compromised, it won't be valid for an extended period. Use a short-lived token life, ideally no more than 15 minutes.
  • Set refresh token features to extend the session duration, which allows users to fetch new JWT tokens for an extended period of time. For example, when the JWT expires, the refresh token automatically updates and refreshes the token to allow the customer to have a seamless experience.
  • Refresh tokens also require specific security measures, the refresh token needs to be expired after a reasonable period of time (a few hours to a day).
  • Avoid storing sensitive information in JWTs. Instead, use a separate database to store such information. Only include information in the JWT that is required for authentication or authorization purposes.
  • Implement a mechanism to revoke JWTs when they are no longer needed, such as when a user logs out or changes their password. This ensures that even if a JWT is stolen or compromised, it won't be valid for an extended period. Developers need to code additional backend functionality that will invalidate JWTs instantly.
  • When receiving a JWT, confirm that it is signed and has not been tampered with using a digital signature.
  • Use HTTPS to ensure that the JWT is transmitted securely between the client and server.
  • Store JWTs securely using mechanisms like browser cookies with the HttpOnly, Secure flag, and SameSite attributes set. This ensures that the JWT can't be accessed by JavaScript, and it's only sent over HTTPS connections. Do not store JWTs in local storage.
  • Implement mechanisms to prevent cross-site scripting (XSS) attacks, as they can be used to steal JWTs. Use Content Security Policy (CSP) headers to limit the sources of scripts that can execute on your site.
  • It's important to never render the token on screen, in URLs, and/or in source code. Doing so can allow an attacker to easily obtain JWTs and impersonate users.
  • The "none" algorithm does not provide any signing or encryption, which leaves the token open to tampering. Avoid supporting this algorithm in your application.

By following these best practices, you can securely implement JWTs in your application and reduce the risk of JWT-related attacks.

Conclusion

Securing JWTs is a critical aspect of web application development. Despite their many benefits, JWTs can pose security risks if they are not properly implemented and secured. But good for us, developers can take steps to mitigate potential threats and ensure the security of their applications.

In this post, we went through some of the best practices to secure JWTs. By following these best practices, developers can ensure that their JWTs are properly secured, and their web applications remain safe from unauthorized access and potential attacks.

It is important to recognize that there is no one-size-fits-all approach to securing JWTs. Therefore, in addition to the above-mentioned practices, developers must consider their specific use case for JWT security, and take a proactive approach to secure their applications.

About the author

Omkar Hiremath

Get security insights straight to your inbox

Additional resources

Here to get you started

Featured Post Image
Icon

The State of Penetration Testing as a Service- 2022 Edition

Say goodbye to 300+ page penetration test reports

Providing the quality of the biggest names in security without the price tag and complications.

Book a 30 min consultation

Manual penetration testing

Full time Canadian hackers

Remediation support

CTA background