loading...
Cover image for A Brief Introduction to Securing Applications with JWT

A Brief Introduction to Securing Applications with JWT

livecodestream profile image Juan Cruz Martinez Originally published at livecodestream.dev on ・7 min read

JSON Web Tokens has become the favorite choice among modern developers when implementing user authentication. JWT’s popularity is clearly justified by what it brings to application development. In this post, we are looking deep into JWTs and why they stand out among other authentication options, as well as what you should be concerned about when using them.

What is a JWT token?

A JWT is a self-contained method that can be used securely transmit data between two endpoints. JWTs are most commonly used for user authentication. They can also be used to securely exchange information. In this post, we are covering how JWTs are used for user authentication. However, information exchange using JWT follows roughly the same steps as user authentication.

The authentication process involving JWTs follows these steps. When a user first logs into the application, system backend issues a JWT to the user and sends it to the client-side. This token contains a special signature that validates the token as a one issued by the system. The client stores the token in the browser and sends it with every request to the server, where the token is used to verify the user’s authentication.

A JWT consists of 3 strings separated by periods. The 3 of them are the header, payload, and the signature. Follows is an example JWT token made of these 3 parts.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteXdlYnNpdGUuY29tIiwiaWF0IjpudWxsLCJleHAiOjUxNDM3ODA4MDAwLCJhdWQiOiIiLCJzdWIiOiIiLCJpZCI6IjEyMzQ1OTEiLCJuYW1lIjoiTWFyeSBQb3BwaW5zIiwicm9sZSI6ImVkaXRvciJ9.LYKHdyKj6SnxYaRZH_ZhiW6yk31zaBQehYm4BgawH_o

Let’s see what each of these parts contributes to the overall makeup of the token.

Header

JWT header contains metadata about the token in JSON format. Two fields present in the header are alg and typ. ’alg’ specifies the algorithm used to sign the token when generating the signature, which we will talk about in a moment. ’typ’ specifies the type of the token, which is ’JWT’. A typical token header is shown in the following example.

{
    "alg": "RS256",
    "typ": "JWT"
}

Here, the header states the algorithm used to sign the token is RS256.

The header is stored as the first part of the token after being encoded in base64url.

Payload

The payload of a JWT stores information about the token and any other entity in JSON format. Usually, a JWT used for authentication stores some crucial information about the user, such as the user ID and user role. Token storing user information usually looks like this.

{
    "id": "1234591",
    "name": "Mary Poppins",
    "role": "editor"
}

These JSON fields stored in the payload are known as claims.

In addition, there are some claims that are defined by the JWT standard. It’s not necessary to include all these claims in a JWT, but including at least some of them will be beneficial in most situations. Here are a few standard claims that we can use.

  • iss: Defines the issuer of the token.
  • exp: Provides an expiration time to the token. Once this expiration time is passed, the token is no longer valid.
  • aud: Defines the audience of the token.
  • iat: Stores the time the token was issued at.

Let’s see how a payload with some standard claims looks like.

{
    "id": "1234591",
    "name": "Mary Poppins",
    "role": "editor",
    "iss": "mywebsite.com",
    "exp": 3600
}

The payload of a JWT can include as many fields of information as you want, but it’s recommended to keep the size as small as possible. Also, you should not store sensitive information like user passwords in the payload since it is not encrypted. It’s simply encoded in base64url encoding.

Signature

The last part of a JWT token, the signature, is a Message Authentication Code that is used to verify the token was not modified or generated by an outsider except the authorized application servers.

Signature is generated by signing the combined JWT header and payload using an encryption algorithm and a secret stored in the server. Only someone who has the token’s header and payload and the secret can generate a sign accepted by the server. So, it is important to use a strong secret to encrypt tokens and securely store it in the server.

If we use a symmetric algorithm like HMAC SHA-256, the server issuing the JWT and the server validating the JWT should have secure access to the secret. If an asymmetric algorithm like RS256 is used, we can use a public-private key system, where a private key is used to sign the token and a public key is used to validate it.

As you have already guessed, the signature is the most crucial part of a JWT token. It keeps unauthenticated sources out of the application and keeps everything secure. When the issued JWT is sent back to the server with every request from the client-side, the server checks the signature to validate that it’s a token issued by the system itself and then proceed to serve the client request.


What is so special about JWT?

As we got to know the makeup of a JWT token, you might have been thinking about what makes it so special compared to other authentication methods, especially session-based authentication.

The most important characteristic of JWT that makes it special is its statelessness. We saw what sort of data can be stored in a JWT in the previous section, and all the data that are needed to verify the token and identify the user are stored in the token itself. There is no need to maintain any record of the token in the server, like store the token in a database as we do with sessions. This makes JWTs stateless.

This stateless nature gives us the biggest benefit that comes with JWTs: The server that issues the JWT and the server that validates it does not have to be the same. One server can issue JWTs and carry out authentication tasks, while the other server that implements application logic can validate the JWT independent of the first server.

In the age of API backends and microservices-architecture applications, this allows the developers to delegate all authentication tasks to one server while the others implement application logic, and decouple the system as much as possible.

When JWT issuing and validating is carried out by separate servers, using a public-private key system is the best approach to take. In this case, the private key should be stored securely in the authentication server.


How to send JWT tokens from client-side to the server and vice-versa?

When sending the JWT back and forth between the server and the client, we can send it along with the Authorization HTTP header. However, sending tokens over HTTP connections makes them susceptible to Man-In-The-Middle (MITM) attacks and stolen tokens. Therefore, it is essential to use secure HTTP connections when using JWTs.

Another problem with sending the token in the HTTP header is, in this approach, the token is usually stored in the local storage of the client-side browser. This exposes the tokens to being stolen using Cross-Site Scripting (XSS) attacks.

As a solution to this, you can send the token inside a cookie instead of inside the Authorization header. It’s essential to set HttpOnly and secure flags of the cookie to prevent attackers from stealing tokens using XSS attacks.


Expire your JWT tokens

If in some way a JWT token is stolen by a third-party, they can use the stolen token to access the application and gain the token owner’s privileges. As a solution to this problem, we can set a short expiration time for the JWTs. This way, even if an attacker steals a token, they cannot make use of it for a time long enough to make an undesirable impact.

We can set the exp claim inside the payload to give a short expiration time to the token. However, this action does not eliminate the threat of stolen tokens, just reduces the chances of it leading to a serious attack.

In real-world applications, setting a short expiration time to JWTs is not as simple as it sounds. If we created tokens that expire after ten minutes of creation, it will drastically reduce the user experience of your application’s users. How likely are the users to tolerate having to re-login every 10 minutes they spend on your application?

But there is a solution to this: refresh tokens.


Refresh tokens—what are they?

Since asking the users to log in again after every time a JWT token expires within a short time is not a good solution, developers have found a solution to this in refresh tokens.

The refresh token is a JWT token with a longer expiration time. It is used to issue a new access token, which is also a JWT token but with a shorter expiration time, every time the old access token expires.

In this situation, the access token is the token that is sent back and forth between the client and the server and has a short expiration time. However, when the access token expires within a short time, instead of asking the user to log in again, the server uses the refresh token to generate a new access token. Refresh token stores data needed to create a new access token.

Since refresh token has a long expiration time and it is not being passed to the client-side in any case, we should store them in a backend database. If an attacker somehow gets access to a refresh token, it’s a serious security threat to the system and the owner of the refresh token given their long expiration time. So, refresh tokens must be stored under maximum security measures.

A user first receives a refresh token when they login to the system. Then, the token is stored in a secure database. Issuing a new refresh token every time a new access token is generated is something you can do to ensure that the security impact of stolen refresh tokens is low.

When a refresh token expires, the user has to log in to the application again and get a new refresh token.

If JWT issuing and validating are handled by separate servers, it’s important to note that issuing access tokens using refresh tokens is a task handled by the authentication server.


Conclusion

JWT is a modern and robust solution to authenticating users and sharing sensitive information while not maintaining state. A JWT is made of three parts: header, payload, and signature. Despite the benefits of JWTs in modern application development, there are special measures we need to take to ensure the security of the tokens and user data. Sending JWTs in cookies instead of in the header, shortening their expiration time, and using refresh tokens to issue new access tokens are some of the security measures we can take to guarantee the security of our application, its users, and their data.

In a follow-up post we are going to implement all it on an API.

Thanks for reading!


If you like the story, please don't forget to subscribe to our free newsletter so we can stay connected: https://livecodestream.dev/subscribe

Posted on by:

livecodestream profile

Juan Cruz Martinez

@livecodestream

I'm an entrepreneur, developer, author, speaker, and doer of things. Email List: https://livecodestream.dev/subscribe

Discussion

markdown guide
 

How can JWT be secure, if I can see the content inside it regardless of having the SECRET KEY?

jwt.io/#debugger-io

 

The data in JWT isn’t secure, it’s just signed so you can know whether to trust it. It’s also possible to encrypt data before putting it into a JWT...but if it’s really so sensitive it probably doesn’t belong in there anyway.

 

The attacker can't benefit from having this data, since it doesn't have any sensitive information as mentioned in the article. Any user can 'claim' that he has the permission to do anything till whatever expiry date he chooses but he needs to prove that using the signature which he doesn't have control on.

 

That's because you haven't encrypted the token with any secret key, only using JWT's default setup. It's your own responsibility to add encryption to the token. That said, the token shouldn't be used for secret data, just data relevant to access authorization. The security lies with the signature, which prevents the payload and header from being manipulated, since it would result in a different signature.

 

I am talking about JWT at the top of this article.

The decoded is

{
  "iss": "mywebsite.com",
  "iat": null,
  "exp": 51437808000,
  "aud": "",
  "sub": "",
  "id": "1234591",
  "name": "Mary Poppins",
  "role": "editor"
}

Not sure if it is safe as sometimes it contains email and phone number as well. stackoverflow.com/questions/388975...

It's not inherently safe, no; not unless you apply an encryption scheme to it. But it's not meant to be. JWT, as the name implies, is just a stateless self-contained token used to identify the entity making a request, using a token that was issued by a trusted authority after the entity authenticated itself. It's essentially an immutable receipt for your authentication, that you can then show to the API server that "yes, I am authenticated - this is who I am", without needing any maintained state on the API server. The API server will then use this information to validate whether you have access to the operations your trying to carry out.

The token It's not a good place for transmitting sensitive information in general, and especially not when it's not encrypted.