Demystifying JWT: How to secure your next web app

Kyle Mistele - Jan 5 '21 - - Dev Community

How are you securing your web applications? Are you using session cookies? Third party-based authentication? SAML? Today I’m going to introduce you to a neat standard called JSON Web Tokens, or JWT for short. If you’re worked on web applications, there’s a good chance you’ve at least heard of them, but today I’m going to try to de-mystify them for you.

If you’re interested in getting into all the nitty-gritty details, you can read the RFC, but that’s not the goal of this article. Instead, I’m going to:

  1. Give you a high-level overview of what JWT is
  2. Go a little more in-depth about how JWT works and why it’s great
  3. Cover some common JWT security pitfalls

What is JWT?

JSON Web Token (JWT) is an open standard for creating and transmitting data. It provides a way to cryptographically sign a JSON payload to verify its authenticity and integrity, and/or to encrypt the JSON payload to provide confidentiality. Note that you might sometimes hear cryptographic signatures referred to as digital signatures — they’re two names for the same thing.

A JWT is a cryptographically signed token

For the purposes of this article, we’ll be discussing cryptographically signed tokens. Cryptographically signed tokens are issued by the server to a user, and can then be presented by the user back to the server to prove that the user is authorized to perform an action. There are two primary advantages to this cryptographic signature:

  1. Since only the server knows the secret key, only the server can issue valid tokens.
  2. It is impossible to modify or tamper with the token and its JSON payload without detection because of the properties of cryptographic signatures. (Want to know how that works? More on that here.

These properties make JWTs a great mechanism for authorization: when a user logs in with their username and password, you can issue them a token that contains identifying information such as their User ID, their permission/access level, and other attributes that might be useful.

Then when the user tries and access application routes or functions, they present this token to the server, and the server can read these properties from the token. Once the application ensures that the token is valid (tokens can be configured to expire) and hasn’t been tampered with, you can make authorization decisions based on the information in the token.

Token structure: the 3 parts of a JWT

A signed JSON Web Token has 3 main parts: the header, the JSON payload, and the signature.

  1. The header contains JSON identifying the encryption algorithm used to generate the cryptographic signature, and can also contain other information such as token type, and x.509 certificate chain information if you’re using it.
  2. The payload is a JSON object. The data which it contains is known as the claims. The JWT standard defines seven standard claims. You can think of these as “reserved” claims in the same way that some keywords in most programming languages are reserved to mean certain things and can’t be used for other variable names (examples that come to mind include class if, else, and so forth). These standard claims can store information about the user's identity, expiration information, the issuer, and more. You can also add additional claims to the token at will. I'll cover this more in the subsection below.
  3. The signature, which is calculated by encoding the header and payload with base64, concatenating them together with a ., and then encrypting this string using the server's private key. To verify a token, the server will repeat this process for the header and payload of the token that it received, and then compare the result to the token's signature block. If the token has been tampered with, the two will not match.

To form the token from these parts, each part is base64 encoded, and the parts are concatenated together with dots. Below is an example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI
Enter fullscreen mode Exit fullscreen mode

JWT Claims - storing information in JWT tokens

The JWT’s claims are defined in the token’s payload. They can store useful information about the token, the issuer, the user it has been issued to, as well as other optional information.

For example, when a user logs in, the server checks if they have admin permissions, and then issues the user a token that contains their user ID and says whether they have admin permissions:

{
  "iat": 1609781109,
  "nbf": 1609781109,
  "jti": "0c2df7d5-f940-409a-b8b5-b3c6f9f1ef1e",
  "exp": 1609784709,
  "identity": "964403f4-444a-428a-88a0-15da8cdaf17c",
  "fresh": false,
  "type": "access",
  "user_claims": {
    "email": "example@example.com",
    "real_name": "John Doe",
    "customer_acct": "Some Organization LLC",
    "is_admin": true
  }
}
Enter fullscreen mode Exit fullscreen mode

In this case, identity is a GUID that is the user's identifier. The iat, nb, exp, and jti fields are all standard claims. user_claims is a claim I've added to store additional information about the user.

When the user attempts to perform an action, the server can check the token sent with the user's request, and can use these claims to see if the user is authorized to perform that action.

Benefits of using JWT in your application

Using JSON Web Tokens has a lot of advantages:

  • The web runs on JavaScript, so JSON is a great choice for storing authentication information. But JWT isn’t limited to JavaScript applications — everything from PHP to Python to Go can consume JSON. It’s flexible and easy to use.
  • JWT Claims allow you to easily store additional information about users that you can access within your application without doing database lookups.
  • Tokens are small and URL-safe. They can be stored as cookies, in local storage, or in session storage.
  • Most common web frameworks have libraries for JWT that do all the hard work for you. (I’ll include links to some of these at the bottom of this article).

Common JWT Security pitfalls

Like any security mechanism, JWT has some common pitfalls. They aren’t hard to avoid, but you do need to know what they are in order to avoid them:

JWT is for authorization, not authentication

JWT is a mechanism for authorization, not authentication. The distinction is important: authentication is ensuring that a user is who they say they are. Authorization is determining if a user is authorized (allowed) to perform an action, usually after authentication has already occurred.

Before you issue a JWT token to a user, you should authenticate them — this is usually done with a username and password. (If you want to learn more about that, check out my article on password hashing). Once the user has authenticated (i.e. their username and password have been verified), then you issue them a token that they can use for authorization purposes in subsequent requests to your application.

Make sure your key is secure

If you’re following along in a demo, they’ll commonly have an example key with the example code. Do not copy their key — generate your own instead. Don’t use a short word or phrase — it should be a long, random key.

Don't hard-code your secret key into your application

In order to sign tokens, your server needs to have a secret key that it users. Depending on the JWT framework you use for your language, you may specify this in one of a number of ways. It’s important to not hard-code your key into your application. Hard-coding the key will result in the key being committed to your version control. (This is especially bad if your project is public!) Anyone that has the key can create tokens, so it’s important to keep it a secret. I recommend using environment variables or some sort of secrets manager.

Storing tokens in cookies? Do so securely.

Make sure to set the secure and HttpOnly attributes on your JWT cookies. The secure attribute will ensure that the browser only sends the token over an encrypted (https) connection to prevent the cookie from being intercepted.

The HttpOnly attribute will ensure that the cookie can't be accessed via JavaScript, which will help mitigate Cross-Site Scripting (XSS) attacks.

You can find more info on this here.

Conclusion

Key takeaways:

  • JWT is an open standard that can be used for authorization once your users have authenticated.
  • JWT tokens cannot be forged or modified (without detection), without knowing the secret key.
  • JWT allows you to store JSON data (“claims”) in tokens that can be used for authorization or other purposes
  • JWT is easy to use, and there are lots of great frameworks for implementing it in your applications
  • Ensure that your application manages the secret key and JWT tokens in a secure manner

I hope you find this useful! Let me know what you think in the comments below.

If you’re writing code for cloud applications, you need to go when things go wrong. I helped build CodeLighthouse to send real-time application error notifications straight to developers so that you can find and fix errors faster. Get started for free at codelighthouse.io today!

Footnote

As promised, here are a few links to JWT libraries for Python/Flask, Node.js/Express, and PHP:

Flask-jwt-extended: a highly robust module for Python’s Flask framework that I thoroughly enjoy using.

Express-jwt: a great package that seamlessly integrates into Node.js Express apps. I highly recommend it if you’re building with Node.js and Express.

php-jwt: a high-quality JWT library for PHP maintained by Firebase.

. . . . . .
Terabox Video Player