JWTs: Not All Merry and Jolly

Vanessa Fotso
7 min readJan 23, 2022
JWTs Browser — Server Flow

Session cookies are the most common mechanism for an app to handle user sessions. When a client authenticates to the program, a unique session identification is generated, which is saved on the server and delivered back to the client. Session cookies are saved in your web browser and are reused for each subsequent request to the server to maintain the session. This solution is ideal for dealing with typical successful authentication scenarios. However, the solution requires the server to keep the session’s state and data in memory and it is not efficient for authentication on multiple platforms. Thus a hit on the application’s performance.

To compensate for such shortcomings, utilizing a kind of stateless (session info is not stored server-side) authentication provides for greater application scalability along with more efficient HTTP requests’ authentication. Token-based authentication seems to be the most widely used stateless authentication approach, with JSON Web Tokens (JWTs) leading the trend today.

However, this post will go over the downsides of using JWTs.

What are JWTs?

JWT Graphic

JSON Web Tokens (JWTs) are tokens created by the server during client authentication on a web app and transmitted to the client. The client then uses the generated token with every request to access the server’s protected resources. They are designed to facilitate data sharing between systems while ensuring the integrity of the data and verifying the identity of the issuer.

To verify the authenticity, the information in the token (payload or claims) is encoded as a string and cryptographically signed using a private key owned by the server. When the server receives a request from the client presenting the token for access, the server simply compares the signature of the token to the one it generates using its private key. The token is validated if signatures are the same. The security aspect of JWTs makes them extremely valuable in situations when you need to state that certain data can be trusted by verifying that the issuer is truly who he claimed to be.

JWTs Structure

JWT Structure (taken from jwt.io)

A JWT consists of three parts, which are Base64 encoded and separated by dots (.) according to RFC 7519: header.payload.signature (more detail on the jwt.io pic above).

  • Header: is a JSON object containing the metadata about the token’s signature (typical field here includes alg, which value is the signing algorithm being used, and typ, which value is the type of the token). The header may also contain additional fields. It is sometimes referred to as the JOSE (JavaScript Object Signing & Encryption) header.
  • Payload: is JSON ‘claim’ data, which contains statements about the issuer of the token and other additional data. There are several standard “claim” fields as defined on RFC-7519. Custom or private claims can also be added to the payload, which is the information the issuer wishes to share with other entities.
  • Signature: this is the security feature of a JWT. It is the result of the encoded header and payload concatenated and separated with a dot (.), then hashed using the algorithm specified in the header, and encrypted with the issuer’s private or secret key. The signature allows verifying the authenticity of the token, asserting the data sent along has not been tampered with.
signature = HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

JWTs are usually used to verify that an entity/client/user is who he claimed to be. It is important to note that the JSON data contained in JWTs are usually not encrypted, making the header and payload readable by anyone. Thus, developers should avoid inserting sensible information in the payload or header elements of a JWT unless it is encrypted (check JWE).

Common Use Case

JWTs are generally used as token-based authentication and authorization. Several security libraries use JWTs as API tokens or session tokens.

In the authentication/authorization process, a client sends its credentials to the server, which verifies the client’s identity then issues a JWT as a response. The generated JWT contains important information the client needs for future requests (client’s personal info and/or permissions) and it is commonly stored as a session token on the client-side. The JWT serves as a key for the client to access the server’s protected routes, resources, or services that are permitted with that token. When the server receives subsequent requests from the client with the JWT, it validates the legitimacy of the token then extracts the client’s data from the token to perform the requested action. The server here no longer needs to unnecessary query the database to retrieve the user’s data, check if the user has permission, or keep track of the user’s session state. Thus, the stateless property of JWTs. This feature, if well implemented, can greatly improve the performance of a server as it reduces the load on the server’s database or cache system.

Sample Authentication/Authorization Flow

JWT has great features and a large scope, which increases the potential for mistakes.

Drawbacks

Developers constantly make tradeoffs between security and performance. Although JWTs have potential benefits, they also introduce some security concerns.

Complexity

JWT is a complex standard and prone to mistakes for developers at any level. If not implemented correctly, it may provide the means for someone to attack your system. To validate the contents and obtain the user-id from the token, JWT employs cryptographic Signature techniques. Understanding the signature algorithm necessitates a basic understanding of cryptography. As a result, if the developer lacks this understanding, they may add security flaws to the system. In 2020 for example, a major vendor for security products (Auth0) had a JWT vulnerability in one of their products.

Secret Key Dependency

The best and worst feature about JWT is that it just uses one private Key. If the key gets compromised, it will put the entire system at risk. The attacker may readily access all user data. The only way out of this situation is to create a new Key that will be utilized across platforms moving forward. All existing client tokens would be invalidated and users will have to reauthenticate. The attacker, on the other hand, may have already obtained the information he needed.

Data Exposure

Reiterating to the point that most JWTs are encoded but not encrypted, data in the header or payload is visible to anyone who intercepts the token. Thus, it is recommended not to put sensitive information within the token, particularly if your systems are subject to compliance and governance constraints like healthcare systems for example.

Token Handling on the Client Side

Tokens can be stored in two places on the client-side: in a cookie or the browser’s locale or session storage.

When stored in locale storage, the application is natively protected against CSRF attacks. However, the token might still be exposed in case of XSS vulnerabilities.

When saved in the browser’s cookies, the “HTTP Only” flag can be set to prevent token theft in XSS attacks. The token, on the other hand, will not provide any CSRF protection. Furthermore, this design does not provide smooth interaction across several platforms. Cookies are associated with a domain and cannot be used by another program hosted on another domain.

— Data Overhead

A JWT has a much larger overall size than a standard session token, and it gets bulkier as more data is added to it. Storing more data to a JWT token could exceed the allowed URL length or cookie lengths causing problems. Considering that each request needs the token in it for authorization, a bloated JWT will slow down the overall loading time and hence impact user experience.

The Danger for Server Authentication/Authorization

— JWT Expiration

JWT uses an expiration time system. The expiration time is typically between 5 to 30 minutes. Because JWT is self-contained, it cannot easily be revoked or destroyed without breaching its “stateless” property. This is truly the root of the issue of using JWT for server authentication and authorization.

Logging out”, “blocking an account” or “Revoking an admin permission” does not instantly take effect on the server’s side. In order for the lockout to be completely effective, the application must wait for the token to expire. In the meantime, someone with access to the token can still access the server until the token expires.

A common solution to this problem is to allow the server to store a list of revoked tokens in the database and cross-check the token sent by the client against the revoked list on every request before performing the next operation. But now we’re making an additional call to the database to see if the token has been revoked, which defeats the point of JWT entirely.

Bottom Line

Developers should critically choose where and how to leverage JWTs, depending on the nature of the requirement and their individual business needs. Although JWT might be the best solution in some circumstances (server-to-server or microservice-to-microservice communication) if correctly and securely implemented using the appropriate security measures. However, before depending on JWTs for authentication and authorization, the aforementioned drawbacks should be considered.

References

Introduction to JSON Web Tokens

JSON Web Token (JWT)

JSON Web Tokens (JWT) are Dangerous for User Sessions — Here’s a Solution

Why JWTs Are Bad for Authentication — Randall Degges (Head of Dev Relations, Okta)

--

--

Vanessa Fotso

Health IT Software Engineer with broad technical exposure and passion for learning.