DEV Community

loading...
Cover image for Demystifying JWT: How to secure your next web app

Demystifying JWT: How to secure your next web app

kmistele profile image Kyle Mistele Updated on ・7 min read

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.

Discussion

pic
Editor guide
Collapse
dbanty profile image
Dylan Anthony

Important to keep in mind that the payload is signed but not encrypted so don’t put anything in there you don’t want the user to see.

Also this token is (likely) going to be sent with every request so try to keep the size down.

Also also python-jose is a more general purpose JWT library you can use in Python.

Collapse
hansyes profile image
Anselmo Martín

Yes, I always say JWT is like a glass box, if is Broken it's invalid. But all can see inside.

Collapse
omawhite profile image
Omar White

This is a really good analogy thank you.

Collapse
eslachance profile image
Évelyne Lachance

JWTs should not be used to store authentication cookies for sessions. They are larger, slower, and vastly less secure than cookies. More details on why right here: gist.github.com/samsch/0d1f3d3b474...

TL;DR: JWTs became popular because of a marketing stunt and now people use them without truly appreciating how wrong it is to replace cookies with them. They have their application in cross-domain stuff, but are not an appropriate cookie replacement. Cookies don't need replacement, they're fine.

Collapse
kmistele profile image
Kyle Mistele Author

I respectfully disagree - JWTs aren't necessarily a replacement for cookies. In fact, they are commonly stored as cookies, as I wrote about above.

In fact, comparing JWTs to cookies doesn't quite make sense:

A lot of people mistakenly try to compare "cookies vs. JWT". This comparison makes no sense at all, and it's comparing apples to oranges - cookies are a storage mechanism, whereas JWT tokens are cryptographically signed tokens.

They aren't opposites - rather, they can be used either together or independently. The correct comparisons are "sessions vs. JWT" and "cookies vs. Local Storage

I've built plenty of applications that store JWTs in cookies, so there isn't necessarily any overhead that you wouldn't have with cookies. The maximum size for a cookie is about 4 Kb, which is absolutely tiny given that most network speeds are measured in hundreds of megabits per second or even in gigabits per second. It's also an immaterial amount of memory given that most programs run with gigabytes of memory. Any other performance overhead is going to be so small that it's completely unnoticeable to the end user.

Using JWTs securely with cookies is entirely possible, since per the MDN specs I linked to, there are multiple mechanisms you can use to mitigate the effects of XSS on session cookies.

There's definitely something to be said for avoiding localStorage and sessionStorage if you're worried about XSS, but the most correct answer to that problem is to have good coding and security practices that prevent and mitigate XSS, since XSS can result in a complete takeover of your site.

I would personally hesitate to call an IETF RFC a marketing stunt. I suspect that many developers are moving towards using JWT because the JSON payload is easy to consume with today's modern JavaScript stacks.

It's also worth noting that I'm not advocating using JWTs for sessions - most modern RESTful APIs and the web applications that are often built on top of them don't have a concept of sessions (since RESTful APIs are by definition stateless).

Collapse
stunaz profile image
stunaz

I would say that it depends on the use case. The most common case is for authentication/authorization. In that case if you store in cookie, IMHO why do you even need JWT in the first place. Done a tons of reading and came to the conclusion that jwt is just not secure for the web, works for a phone/tablet, gui apps.... not for the web. And whatever you do for trying to make it secure. it will look like you are implementing the old session/cookie stuff.

Having building many application with jwt cookie does not really mean anything, yeah it works, but you could also have done it simply with sessionid on cookie at this point.

Thread Thread
steventhan profile image
Steven Than

JWT thrives in microservices setting, it's a PITA having to validate session per service, especially when you have a remote authorization server

Collapse
kylereeman profile image
KyleReemaN

Please be aware of xss if you save the token in localstorage and csrf if you save the token in cookies even with httponly and secure flag. See samesite attribute (lax/strict).

Collapse
mestrak profile image
MeStrak

Nice article, thanks!

I also recently wrote one on mocking a JWT /JWKS in automated tests which I think compliments your article quite nicely: dev.to/mestrak/testing-secure-apis....

Collapse
bevilaquabruno profile image
Bruno Fernando Bevilaqua

Wow, awesome article, i will recommend it to some friends. 😉

Collapse
kmistele profile image
Collapse
erescobar profile image
erescobar

The article is really good but JWT is something that has been on the table for a while and you are presenting it like something really new.

Collapse
monfernape profile image
Usman Khalil

It still presents a different perspective that few people might find easy to understand

Collapse
aiosifelis profile image
Andreas Iosifelis

Very good article, thanks! I would also like to add that it is impossible to invalidate a jwt token before it expires without using a database or changing the secret key.

Collapse
kmistele profile image
Kyle Mistele Author

Yep! A common way to deal with this problem is to use a redis database or similar to store tokens you've marked as invalid, or I've even seen modules for Python that will track revoked tokens in-memory (with a potentially significant performance overhead).

Collapse
drgrey profile image
Dr-Grey

Thanks for the write up and good comments which almost make up to a new article .