loading...

Be careful of the JWT hype train

darraghor profile image Darragh O'Riordan Originally published at darraghoriordan.com on ・4 min read

I’ve been researching using node as a back end for a few months now and SO MANY node articles, courses and project “starters” on GitHub suggest using JWT on your client facing API as a session token.

I think there's way too much hype around it and people are using JWT because it's shiny!

🔐 What is JWT? 🔐

JWT is a JSON object that gets signed in a standardised way. This signed object or token can then be sent to the user through any mechanism. It’s usually returned in the body of an HTTP response or in some header like a cookie. The client sends this back to your server where you check the signature and use the data provided if it's a valid token.

The idea is that token has all the details about the user and their permissions for resources on your API stored in it. Your API doesn’t have to hit another resource to get data when the user sends a valid JWT with a request for some resource.

This is in comparison to a simple session ID that is sent to the client, usually in a cookie. Which the client sends back to your server with each subsequent request. The server validates the session ID against a list it keeps in your database. Then it looks up whatever it needs to about the user to fulfil the request. All state remains on the server/database.

JWT is a terrible solution for client sessions on a web application.

“It will make my API stateless”

This is when you plan to put all the user data and permissions etc in the token so you don't have to make a database call to get user data on your API. Sounds great but...

It's probably premature optimisation

If you’re building a client server web application and you expect less than ummm say 4,000 requests per minute to the database, then the LOWEST paid (\$50/month) tier Postgres and a cheap dyno on Heroku can handle that for you no problem. You don’t need stateless anything and you don’t even need memcached or Redis.

Even the completely FREE Heroku tiers should be good for at least 120 requests a minute. You should upgrade if your project is that popular (and congratulations on the success!).

Unless you’re expecting significant scale then for almost any company’s product you can scale up database sessions until you have enough cash and engineering talent to add something else. Don’t prematurely optimise.

Is your API truly stateless for user data?

It’s VERY difficult to avoid state in a useful client to server web application. Do you really retrieve nothing else about your user from the DB on each request? No role changes or payment status changes might have occurred since the JWT was issued? No intersections between the user and the specific request at all?

Like it's possible if you have a micro-services architecture or something but unlikely in general.

You can't implement stateless basic account administration

Many articles will show you how to setup and login with JWT but they ignore the hard parts - Logging users out and blacklisting users. Acceptable solutions for these features involve maintaining state.

If you want to legitimately have a user “log out” then you need to keep a list of JWTs that have been invalidated by the user. Now you have state that is checked on every request.

If you use a salt to sign each users token so you can later change the salt to log a user out, then you have to check a list of the salts each time the user makes a request and now you have state that is checked on every request.

If you want to be able to block a user because their account is in debt, deleted, or they are up to no good then now you need to keep a list of blocked users and you have state that is checked on every request.

If you increase the transience of the JWT to support log outs then you have a user logging in every 5 minutes, a terrible user experience and probably no users.

Stateful JWT - "I just store the user Id in my JWT"

Yes, you can put a user identifier encoded as a JWT in a cookie and use it like a session cookie but this is server sessions with JWT. Why bother with the JWT hassle? Just use some kind of uuid from a session library and be done with it.

“JWT is supported in all these frameworks and works better across both browsers and mobile clients”

So are cookies. Cookies are just an HTTP header. Any HTTP client can read and set headers. The cookies header also has 20+ years of security and functionality built in to it for browsers (HTTPS only, expiration, site scope, blocking access from JavaScript) and there are well known and understood fixes for issues like CSRF tokens for forgery.

Every back end web framework supports HTTP headers and in fact probably has first class support for cookies, with a sessions library (via a generated id) tied to a data store of some kind.

“JWT is secure”

The signing and verification is pretty secure. However many articles and courses describe storing your JWT in local storage. This is not secure. Any JavaScript on the page can read it and use it.

You almost certainly have JavaScript on the page that you didn’t write that came from an NPM package or a CDN. Vulnerability injection in this way has been done before and will happen again.

The alternative to local storage is storing the JWT in a cookie. So now you need to protect the cookie just like you would with an old school session Id.

So what should you do?

Well you probably don't need JWT. JWT has its uses but there's a good chance it's actually the wrong solution for your application and it's making things more complicated or insecure than a session store with Ids and cookies.

So just be sure you know why you're using JWT and understand its limitations before adding it to your awesome new app!! 😊

Posted on by:

darraghor profile

Darragh O'Riordan

@darraghor

Hi! I'm Darragh ORiordan. I live and work in Sydney, Australia enjoying the mountains and the ocean. I build and support happy teams that create high quality software for the web!

Discussion

markdown guide
 

I think there's way too much hype around it and people are using JWT because it's shiny!

As our industry shifts our paradigm from monoliths to microservices, knowing JWT is quickly becoming a requirement. Sometimes it is good to use the new shiny toy as an excuse to learn it. Even if your app serves less than 4,000 requests per minute and JWT would be overkill for your application, learning JWT and having practical knowledge of it is also of value.

Many articles will show you how to setup and login with JWT but they ignore the hard parts - Logging users out and blacklisting users

This is one area that is definitely overlooked and also complex. The most common solution for this is on the auth server itself, which will keep a list of users that are authenticated, expiring them when they log out. On each request the token will need to be checked against the auth server to see if the token is still valid. This adds significant overhead.

More commonly, most companies will decrease the expiry of the JWT, so it must be refreshed more often. This is due to the difficulty and overhead of managing a true logout experience.

“JWT is secure”

JWT is secure, but it is at the same time less secure than session based authentication. For example, the JWT is more vulnerable to hijacking and has to be designed to prevent hijacking. An unexpiring JWT can become a security risk.

You are also trusting the token signature cannot be compromised. This can happen if you are using weak encryption, encryption that becomes vulnerable in the future, or having the the private keys compromised. This vulnerability doesn't exist with sessions.

So while JWT is secure, it introduces new attack vectors that need to be considered.

There are valid reason to use JWTs:

  • API back end - Your site is static and your back end is an API.
  • Micro-service architecture - A very common way of authenticating across disconnected systems.
  • To Learn JWT - Implementing JWT in smaller projects is a good way to start learning JWTs.
  • Externalizing your authentication to a 3rd party provider like Auth0.

JWTs do not come without their own complixity:

  • Security is more complex and needs to be understood.
  • Just like how micro-services add complexity, JWT adds the same complexity as the auth is disconnected.
  • Simple things like logout become complex and might require changing your expectations and business requirements.
  • Doing JWT right is hard.
 

API back end - Your site is static and your back end is an API.

How can you avoid any curious customer don't call console.log() to show the jwt-secret?.

Client-side security is usually a bad joke.

Micro-service architecture - A very common way of authenticating across disconnected systems.

IP Whitelisting unless the microservice is based on dynamic IP.

 

How can you avoid any curious customer don't call console.log() to show the jwt-secret?.

You don't keep secrets in the JWT. Everything in a JWT should be considered public.

Can you tell me again, why we need JWT encryption?

JWT works with :

Ok, here is the key and it is unique, if it is stolen, then you are screwed. I will crc your key (and encrypt the crc) just for security.

Versus session works with:

Ok, here is the key, I will write down the key and if you don't use it for an hour, then I will invalidate it and you must ask it again. However, if you lose the key, then I can invalidate it, and since I generate the key, then it is safe.

Can you tell me again, why we need JWT encryption?

First of all JWT is not encryption. It is authentication and authorization.

I can't tell you why you need JWT because I do not know anything about your application. But I can tell you why I am using it.

The environment I am working in consists of multiple disconnected systems, each maintained by different departments. JWT allows us to to create an authentication service that is disconnected and also works across multiple systems.

Ok, here is the key and it is unique, if it is stolen, then you are screwed. I will crc your key (and encrypt the crc) just for security.

This is a complete misunderstanding of JWT and realky has nothing to do with JWT.

Encryption (checking the token contents against the signature) works to ensure that everything in your JWT token is valid and hasn't been altered. So it's public, but you can trust it.

That’s not encryption, that’s just signing. The data in a JWT is base64-url encoded, but otherwise easily readable.

There is a standard for JWT encryption, but that’s a whole extra level of pain.

 

The Jwt is for your back end, not your UI. The web client doesn't need to validate it, only pass it along with it's requests.

 

As our industry shifts our paradigm from monoliths to microservices, knowing JWT is quickly becoming a requirement.

Unless scaling your system has driven you to microservices as a point of last resort, they're usually the wrong answer. Over the next couple of years, people are going to figure that out the hard way.

I don't know why JWT would be a requirement for microservices, though. A user connects to an HTTP server, the JWT token is checked there, and the HTTP server connects to other microservices with the user information as part of its request. There's no reason to pass the JWT around.

 

Unless scaling your system has driven you to microservices as a point of last resort, they're usually the wrong answer

I am going to disagree with this statement. It sounds like you are suggesting JWT is a BAD option and should only be used as a last resort. JWT is just an option. And what you choose is will depend on your specific application and needs. So there are times when JWT is the BEST option.

To make a blanket statement saying JWT is always a BAD option is just not valid.

I don't know why JWT would be a requirement for microservices, though.

No technology is never a requirement, but an option... A tool available to us to choose and use.

There's no reason to pass the JWT around.

Blanket statements like show extreme bias and because of this bias become easily dismissed. To prove "no reason" is impossible. There's always a reason.

I wasn't saying that JWT was usually the wrong answer. I was saying that microservices are usually the wrong answer.

Can you provide a scenario where passing a JWT around among microservices is a good idea?

Can you provide a scenario where passing a JWT around among microservices is a good idea?

I'm starting to feel like I am being Sealioned here.

Sealioning

Not my intention. I genuinely would like to know of such a scenario, since I can't think of one.

 

Static site with API backend is not a use case for using JWT.

I use “static” frontends regularly with GraphQL backends and still use sessions with secure httpOnly cookies so they can’t be accessed by the browser.

IMO JWTs are okay for what I refer to as “loose” authentication (when you quickly want to hide some nonsensitive data behind a login using a service such as Auth0 but your not necessarily exposing sensitive data).

Always, when exposing potentially sensitive data, use sessions with secure httpOnly cookies.

 

Static site with API backend is not a use case for using JWT.

it is also not NOT a use case.

I like JWT when you have multiple distributed systems that need to share a single authentication that a single system doesn't have the authority to maintain itself.

Authentication as a Service.

 

Good article, but I can't agree on the ideas you brought up. An http cookie is a technology that originated from the browser world. JWT is agnostic and is not bounded to any platform or technology. We use JWT to store userId only. Sure both cookies and JWTs can be used in similar ways, but it's all about the intention of the code/solution you are building. And when using JWT the intention is a universal token that can be used anywhere and stored any where and not a cookie.

 

And the security of localStorage is not the joke that cookies are. blog.meteor.com/why-meteor-doesnt-... But when your entire argument is based on 'its probably good enough', then the presentation of new evidence is unlikely to change a thing. So have fun with your cookies!

 

I think there's way too much hype around it and people are using JWT because it's shiny!

I think they all suggest JWT because it's easy to setup and lots of tutorials about single page applications just re-iterate that since it can be controlled by JS.

So are cookies. Cookies are just an HTTP header. Any HTTP client can read and set headers. The cookies header also has 20+ years of security and functionality built in to it for browsers (HTTPS only, expiration, site scope, blocking access from JavaScript) and there are well known and understood fixes for issues like CSRF tokens for forgery.

Exactly. That to me is the weakest argument

There was a really interesting discussion one year ago about using JWT and the local storage here if you're interested:

This is my favorite resource so far on the argument JWT and sessions: Stop using JWT for sessions, part 2: Why your solution doesn't work

ps. dev.to doesn't use JWT for the session, just to store additional user info.

 

I've been fighting several days to convince the other developers of the startup that I work for that JWT is not the ideal solution for authentication, I'm happy that there are other people who are talking about this :D

 

JWT is also praised because it features multiple algorithms of codification. It even shows it on its homepage.

Multiple algorithms is not a feature but a problem. We don't need +10 methods of codification but a single one that works everywhere. And so far, only HS256 and HS384 are cross-compatible.

One of the problems of the cookie-session is the session hijacking. JWT doesn't solve it, either for XSS or CSRF. So, what's the objective of JWT?.

Finally, if we want security then we must use SSL. And if we use SSL, JWT is turned redundant.

 

Actually, it's assumed that generation of JWT and exchange of it has to be conducted through SSL for security reasons.

So I don't really think JWT is redundant since it's use case is more suited for API gateway backend to authenticate API services of a given user.

 

But JWT assumes that both parties know each other, JWT doesn't address that problem.

Nah pretty sure SSL doesn't work this way.

Since that technology just provides you a encrypted tunnel.

It does not identify you as who you are as a user besides basic information of the computer you are using.

No, the raison d'etre of SSL is that each communication is safe, so it can't be read or modified by a third party, exactly what JWT promises.

Yeah you are assuming that it identifies the user who are using it but the SSL only identify the computer itself is being used but the problem is that you can have multiple ppl using the same computer. So it is not that specific to the point of that it identify the correct information the user is using at the specific point of time

😵 in a nutshell, we already have session and ssl, the encryption is done by ssl and the identification is done by the session (or cookies or token). SSL is not about identification but that it is possible with a session that it doesn't require a special tool to configure.

So, why we need JWT? To replace ssl and session/cookies? Not really, we still use ssl and or session.

JWT allows a way to not have to check a central DB for every request to say an API. Albeit not w/o drawbacks as covered by this piece. So instead of -- 1) receive request, 2) look up user's session for info and to determine if it's valid, 3) serve user request -- with JWT you can eliminate step 2.

As the author describes for most small and medium sites, validating a request against a central session store is really not that onerous. It's only when you get into Netflix territory - then you have to worry about how to distribute all that state where it's always available and accurate in real time for any service that needs to authenticate the user.

 

I think a big part of JWT should be some type of API Gateway.

In an example case (microservices) we take an API key from the client and the API Gateway creates a signed JWT with relevant user info. This is where blocked/logout checks will be handled.

With this, the JWT gets passed to the corresponding API service and it won't have to check again with the API Gateway on the given JWT. The JWT can also be passed to subsequent services if needed on behalf of the parent service.

1 call to user info is needed in this scenario. And the N microservices within the API request scope doesn't not need to check user auth.

 

API Gateway? Sounds more like an auth server.
"The JWT can also be passed to subsequent services if needed on behalf of the parent service.", that's a risk not a feature.

 

i havent dealt with this situation before
but what the auth api gateway .. will hand to the services ? the user id right ? will that be on the header ?
thank you

 

Great observation.

I would refute with two points:

1) Your application doesn't need it now but if it's a real application then you should always be getting ready for the future. JWT support is extremely easy to add now-a-days as any production-ready web framework will support out of the box or with a plugin.

2) JWT is a common standard for handling several different concerns. Having it implemented out of the box means it will already be there when you actually do need it.

I personally design everything with a scaling-first mentality. It's easy to neglect scaling until your awesome app crashes at the awesomest time ever and you lose all of your awesome users and you awesome clients don't feel awesome about paying you an awesome amount of money.

 

I don't see how cookies are going to stop your scaling? Get user info and store it in a cache with TTL.

 

But how do you check wether an user is authenticated or not in a single page application? What if I'm running an app and web app on the same API?

 

But how do you check wether an user is authenticated or not in a single page application?

It's quite straightforward:

  • on login the client receives a successful response, a secure httponly cookie is set by the server and that's it (the user is now authenticated)
  • the cookie is sent by the browser at each request
  • on logout the server invalidates the cookie, the browser forgets it and you can logout the user
  • you can still use jwt and the local storage to store additional user's data, just not the session cookie

What if I'm running an app and web app on the same API?

what do you mean?

 

Android App & Website sharing the same API & Authentication service, how are you supposed to store a cookie on an app?

You're not bound to have the same authorization mechanism for both services if it doesn't suit your Android app, but you can:

how are you supposed to store a cookie on an app?

A cookie is just a header (not much different from the Authorization header OAuth2 uses), your app has a HTTP client, they usually handle cookies easily.

I'm quite sure there's a way to store a cookie in Android, by Googling I found these:

I do not know how up to date such info is because I have zero experience developing on Android but I do know that whenever you have a decent HTTP client, you have support for cookies (they are not a new technology ;-))

If you're sending http requests with headers back and forth than you must have cookies because cookies are essentially just a header. Right?

 

You don't need JWT, so don't take it that way; but I think you've perhaps had some difficulties and you're perhaps missing a thing or two:

  • on transience: a session cookie has transience too -- and it can be stolen just as effectively as a jwt. Anything which has "physical" access to the document can hijack cookies or localStorage. Anything "on the page" can steal my auth cookie too. Cookies won't save you here.
    • naturally, the fix is to make your jwt have a short lifespan and include a renew token which is automatically used when a web call encounters a 401. Handling expired tokens isn't really your problem -- there are already good frameworks to deal with this, and good auth providers (like Auth0) so you can have someone else deal with this complexity and the security of your user data in a professional manner.
  • No-one said you had to include the JWT as a bearer token. You can use it however you like, including as a cookie
  • There must be no secret data in a JWT. If there is, you're doing it wrong. The point of the JWT is to allow the front-end to make display decisions based on the user's scopes. Your backend still has to implement real security. The front-end should be able to validate the JWT, if it really wants to, by hitting and endpoint. NO JWT SECRETS MUST BE AT THE CLIENT. You should be using something like the OpenID Connect flow, where your login and secrets are done server-side, giving back a token to the web client. The other flows are meant for server-to-server processing, so if you're using them in the client, yes, you're doing it wrong and yes you're not secure at all

All of this is not to say you can't (or shouldn't) use session-based auth -- go ahead! JWTs just make it easier to:

  • make client-side display decisions for the user (eg if she doesn't have access to the admin area, don't show the links)
  • centralize your authentication and authorization in one service which doesn't have to be duplicated across web apps. This service can be queried for token validation as and when necessary.

Doing JWTs properly is hard. I've done it a few times -- and wrong a few of those times! But where I'm standing, the friction to get them working with something like IdentityServer is so low and the benefits for "heavier" client-side web applications outweigh the small setup cost.

 

Great article. Good points on JWT and how much attention it gets while not being the complete solution some make it out to be. I think you are also making a good point about shiny new technology in general. It happens a lot where something comes out and becomes the new big thing and it gets misrepresented as something it is not.

 

Thanks for all the responses. Love the community here. Some interesting points and discussion.

I just hope this article might make people consider if they are using JWT to solve a specific problem for their application rather than just using it by default :) Especially people new to web development using a "starter" or doing a course that only shows them JWT in local storage.

Quoting joelnet - "Doing JWT right is hard." !

 

there are well known and understood fixes for issues like CSRF tokens for forgery.

These also add a stateful component to the API. If you want people to still be logged in after node restart or if you ever need to scale beyond one node, then you have to make sure your sessions/CSRF tokens are stored in a shared database (e.g. Redis). This creates extra failure modes to handle (session store down or overloaded) vs verifying the JWT signature. Session cookies are a great solution for many cases, but it has trade-offs too.

 

A brand new approach regarding the usage of JWT, which is the combination of the two very famous security mechanisms JWT and TOTP for the first time in their digital lives.

check it out

 

No role changes or payment status changes might have occurred since the JWT was issued?

Can't we revoke JWT in that case?

By no means a JWT advocate btw.

 

Then you have a stateful revocation list you have to make available to every server and JWT is no longer stateless.

 
 

Article about JWT. Ctrl-F "PASETO". "No results found".

Am dissapoint.

github.com/paragonie/paseto

paseto.io

 

All good, but - what about "microservices" architecture? Where several servers cannot validate the cookie session? Do you have a solution other than JWT?

 

Assuming you need the JWT for user properties because of your decoupled stateless architecture, just have another property on the JWT that holds a key.

Assuming you have a layer in your architecture that all your microservices use for config etc. Redis for example.
The value for that key in Redis could be the token.

 

This is an odd misconception I keep seeing about microservice architectures. Typically you have a gateway where requests arrive. The gateway verifies the session and forward the user context that it verified to any other systems it needs to contact.

This is how all the big companies that came up with the notion of microservices like Google and Facebook work.

 

Branca

tuupola / branca-spec

Authenticated and encrypted API tokens using modern crypto

Branca Token

Authenticated and encrypted API tokens using modern crypto.

What?

Branca is a secure easy to use token format which makes it hard to shoot yourself in the foot. It uses IETF XChaCha20-Poly1305 AEAD symmetric encryption to create encrypted and tamperproof tokens. Payload itself is an arbitrary sequence of bytes. You can use for example a JSON object, plain text string or even binary data serialized by MessagePack or Protocol Buffers.

Although not a goal, it is possible to use Branca as an alternative to JWT. Also see getting started instructions.

This specification defines the external format and encryption scheme of the token to help developers create their own implementations. Branca is closely based on Fernet specification.

Design Goals

  1. Secure
  2. Easy to implement
  3. Small token size

Token Format

Branca token consists of header, ciphertext and an authentication tag. Header consists of version, timestamp and nonce. Putting…

 

'makes it hard to shoot yourself in the foot', now you need the secret on the client to decrypt?