Is JWT really safe?
Spoiler alert: No, it’s not.
Have you ever wondered what happens behind the scene when you log into a web app (Facebook, Twitter, …) using your username and password? After verifying your username and password, the server will issue you a string-based token, which you can use to fetch other web resources without having to authenticate again for a predefined period of time.
The general concept behind a token-based authentication system is simple. Allow users to enter their username and password in order to obtain a token which allows them to fetch a specific resource — without using their username and password. Once their token has been obtained, the user can offer the token — which offers access to a specific resource for a time period — to the remote site. Using some form of authentication: a header, GET or POST request, or a cookie of some kind, the site can then determine what level of access the request in question should be afforded. 
JSON Web Token (JWT)
JWT is a special type of token that is structured in a way that makes it suitable and convenient for securely transmitting information between parties as a JSON object over the web. Its characteristics include:
- It is represented as a string. Example:
As a string, it is easy for client to pass JWT token in a request header and for server to parse.
- It contains JSON-encoded data. This means that you can encoded any JSON object into a JWT token and decode the token to retrieve the original object, making JWT suitable for transporting information about a user’s identity, such as user ID, token expiration time, etc.
- It is signed. This allows the any trusted party with the token to verify whether the token has been modified or changed during transmission.
More information about JWT can be found on its official website.
JWT in Action
What makes JWT stand out among traditional session identifiers is its ability to directly contain user data in the form of JSON. As JWT is stateless, it facilitates the use of RESTful API, allowing each request to be authenticated and served without depending on others using the JWT token sent in the
When the server receives a JWT token, it can perform validation by using the secret key that was used to originally create the token, avoiding the complication of storing session information.
Here is how a JWT token’s lifecycle looks like:
- A user visits the login page of a website. He enters his username and password then clicks Login.
- The server receives the request, check the database to verify the provided credentials. If all good, the server generate a JWT token, which contains data about the user and the token itself, using a predefined and common secret, then send the token back to the client.
- For all following requests, the client sends the obtained JWT token in the
- For each request, the server parses and get the JWT token in the header, decodes it to validate and verify the user’s identity and permission. If all good, it serves the request.
- Once the JWT token expires, the user is required to login again using his username and password.
And that wraps up our brief introduction to JWT. Now to avoid making the title of this article a clickbait, we will go on to examining the safety of JWT.
As we intuitively know, losing our username and password to an attacker is really dangerous, as they would be able to impersonate us and get full access to our accounts. This can be mitigated and prevented using Multi-factor Authentication (MFA), as SMS verification, Face ID, Touch ID, app-based authorization, etc., are much harder to be compromised.
However, the same story can’t be applied to JWT. If someone manages to hijack our JWT, he will obviously bypass our MFA and straightly get full access to our accounts to perform API calls. This is due to the fact that a JWT will only be issued after the rightful user has completed MFA checking; thus, when an attacker gets the token, he will no longer be challenged by the MFA.
Therefore, a compromised JWT is even more dangerous than a compromised pair of username and password. You might say, JWT is safe because it has an expiration time, after which a token will be invalidated. Correct, setting the token short-lived is a good practice in preventing JWT hijack, but it won’t completely wipe out the risk, because if the attacker is able to steal your JWT once, he can do it again and again without much work.
One way attackers could use to get your JWTs is through Cross-Site Scripting (XSS). If JWTs are stored in localStorage or sessionStorage, attackers might use XSS to claim the tokens and hijack sessions. Therefore, do not store sensitive information in Local Storage, as it is used for user convenience by saving website states and settings.
JWT Good Practices
- Set token expiration time small.
- Rate limit your requests. This means allowing only a certain number of requests per IP address per second.
- Keep an eye for unusual behaviors. For example, if your user has logged in from location A for several hours, and suddenly the requests come from location B far away, you should immediately prevent those requests from being served, revoke the token, and ask your user to reset their password, etc.