Hacker News new | past | comments | ask | show | jobs | submit login
[flagged] JWT Tokens are NOT safe (redislabs.com)
106 points by node-bayarea on June 25, 2021 | hide | past | favorite | 107 comments



This doesn't only apply to the way JWT tokens are usually used for sessions (no persistence). The default session store for Devise (Rails) and .NET Identity is cookies, on the client. They are encrypted with a secret key and decrypted for authentication. Identity in particular allows you to store any number of "claims" in the cookie, such as a username or role. Because the cookies are signed and HTTP only, this is safe from attackers, but this method, along with pretty much any method that isn't storing some sort of state on the server, has the same 3 problems listed in the article.

   1. Logout doesn’t really log you out!
   2. Blocking users doesn’t immediately block them.
   3. Could have stale data
I know there are ways around this with a really fast refresh time, or as I've heard, storing some sort of signature in the cookie, but I personally prefer a plain old server-side session store with something like Redis, or even just an in-memory HashMap. Authentication doesn't have to be that complicated.


Regarding Devise + Rails cookie session store:

1. Logging out deletes the cookie, you are really logged out. If your session cookie got stolen, you have other issues but I don't think this is really a matter of being 'logged out'. It is pretty easy to implement 'revoke all sessions for this user' type of logic with Devise and Devise does this of the box when a user changes their password.

2. Permissions are orthagonal to Devise. Devise stores the user ID in the session and loads the user model on every request, any permissions / blocking system would chain from there.

3. I can't think of anything that devise stores in the session where staleness would matter, other than things intended to be checked for staleness, like the salt that is used for the aforementioned revoke all sessions on password change functionality.


For 2 and 3 I was mainly referring to Identity, although I'm not sure how it works internally. For 1, I think the main issue is that when someone logs out, or you log someone out, you aren't guaranteed that they are actually logged out. There are cases where this does matter.

How does Devise handle 'revoke all sessions for this user'?


> How does Devise handle 'revoke all sessions for this user'?

The cookie has both user id and a special token which IIRC is a substring of the user's password salt. Retrieving current user from cookie includes not only looking up by id, but also verifying the salt. So if you change the password, the salt is also changed and all the old sessions will stop working.


Ah okay, so Devise does lookup the user in the database to authenticate. I guess it's not applicable to my premise then.


This is exactly the point of the blog. If you are using JWT + Workarounds to make it secure, then you'll loose all the benefits. This is why virtually no one in fin-tech use JWT.


In general, I agree that sessions should be opaque tokens stored in an http-only, strict same-site policy cookie. I just had a few problems with a couple of the arguments:

> 3. Could have stale data

The only use case I've seen for storing authorization information in a JWT is for something like OAuth2 scopes, which is different than strict authorization rules. They're more like delegate rules, but you should only treat those as a first line of defense before you do the checks that the authorizing user actually has access to.

Also, it's just as easy to let a redis cache go stale. Seen it more than once with this same security issue.

> 4. JWT’s are often not encrypted so anyone able to perform a man-in-the-middle attack and sniff the JWT now has your authentication credentials. This is made easier because the MITM attack only needs to be completed on the connection between the server and the client.

If someone can MITM your connection in plaintext, they have your credentials, whether or not you use a JWT. Yes, any information you encode in a JWT is plaintext, so if you put personal information in there, consider it leaked. Am I missing the argument here?


Holy hyperbole, Batman.

This is just a straight marketing post for Redis.


Yep if you look at the bottom paragraph it's a marketing advertisement.

I suspect the engineers that helped write this article are either very strongly for redis or cringed knowing their content would be used this way.

No system is 100% secure.


The irony is: redis and memcached are not great for storing sessions either.


Why not? Every large system I’ve ever seen used one of these for sessions.


> JSON Web Tokens (JWT) are Dangerous for User Sessions—Here’s a Solution

Actual title on the website.

Edit: To watch clickbait work in real time, check OP's submit history.


@dang might want to check into that, OP seems to have a history of forcing submissions through.


They posted this twice for some reason


The first post had the original title and it had 5 votes. Now this clickbait title and it blew up.


> Security should be binary. Either technology is secure or it’s not.

From my experience, that's not the case for almost anything. In fact, I'd consider it a dangerous position.


Nothing is "secure or not" - technologies/mitigations are secure against particular attacks. HTTPS is generally secure against passive network eavesdropping, but does nothing to stop local file inclusion in a web app.

Just because there are attacks or ways around a particular defense doesn't mean it's worthless, that's why we have defense in depth.


This. That's why they call them 'attack vectors'.

I disagree with the main thesis for why JWT is a problem. JWT isn't necessarily encouraging you not to hit the DB for user lookup. This is the claim the article makes as a problem with revocation.

It reads like a really long thoughtful article based entirely on false assumptions for how to best use it.

It's ok to carry around some encrypted state in your tokens for some uses cases.


Geez, even RSA is not secure if you push this thing hard enough. Cryptography is all about making it difficult to decrypt, not impossible to decrypt. One can easily setup a conspiracy theory that the government can record all the traffic, and decrypt the dump in the future when computers are fast enough.


Exactly. We don’t even know for sure that there isn’t a faster way to factor numbers with classical computers.

If you’re handling sensitive data, you have to understand the tools you’re using and what their relative weaknesses are. Best practices are nice. But it’s far too easy to make a mistake following them if you don’t understand the principles behind them.

“X is always secure” is a great way to ensure X is not implemented correctly.


Ya, I hate to be pedantic too, but security it almost entirely NOT binary. It should be, but it's not.


It shouldn’t be, either. Things evolve around it, definitions change, but also things need to be less secure because of convenience or necessity. An emergency stop button that halts machinery, causes damage to the machines, stops production, but saves lives, can be the target of any number of imaginative scenarios of malicious actors hitting the button to cause damage or be a part of some grand hacker scheme. But we definitely don’t want to secure that until it takes badging in and a 2FA prompt just to use it.


I respectfully disagree with both of you. Security should be binary, within a given set of requirements / implementation parameters and the intended threat model. Security must be binary within the space of “are you authenticated or not” (within the massive context specific web of trust and private keys) is binary and if it weren’t that would be a problem.


Right.

The problem then is that people - Us - all of us here on HN - frequently fail to understand their project requirements or at least fail to anticipate the future requirements of a project - and we’re arguing about what those requirements should be :)

And it’s not just people under-estimating things: it’s too easy to us ambitious-types to think “what if this simple PHP project goes viral and we need to scale to millions of users overnight?” (Answer for the curious: it won’t) - which in turn makes people think using stateless tokens is fine (well, it is fine, actually - provided you have a revocation system; TFA’s title is just clickbait, and the problems are hardly unique to JWT: almost every web platform’s built-in authX system will support stateless user-info and authorisation and use it by default… if not always and by-design (ASP.NET’s ClaimsPrincipal, Kerberos, etc). (Note I’m not referring to server-side session-state (like PHP’s $_SESSION) which is not intended for authX nor does it scale, for reasons mentioned in the article)

The article is a contrarian-sounding ad for Redis, written by a Redis employee, on Redis’ own website. Why are we treating it like news? And why am I even writing this retort? Argh! HN’s content team know how to drive my engagement! I’m doomed…


But security through obscurity is a valid part of a security methodology, and it’s a part that inherently a continuum rather than binary.

For sure some aspects of security are binary. But even with JWT (I agree with the main points of the article), a token with an expiry of 5 minutes but no revocation capabilities is still objectively preferable to no expiry at all.

The binary aspect of security is primarily about whether you fall within your risk appetite or you don’t.


I don't think you're disagreeing with GP. Maybe it should be binary, but in reality it almost certainly is not treated that way. Trade offs are often made for UX or other business reasons.


Security is binary, as in zeroes and ones that you will never lay eyes on. You seem to be claiming that security should be easy, when in fact the hacks that come will likely be much closer to the metal than you ever get.


No implementation is perfect.


It seems like quite a silly position. By the author's own admission, it is possible to mitigate all the risks warned about. I certainly don't agree that something is never OK to use because if implemented improperly it wouldn't be secure. If I took that seriously, random password generation is insecure, because someone could just use an cryptographically insecure PRNG.


Agreed so much.


OK, but what's the real alternative here? I'm sick of these JWT hate articles on HN all the time with no universal solution.

Why not just use cookies and be done with it? This looks like an advertisement for Redis Enterprise.


You could have short-lived JWT tokens so it wouldn’t matter because they only last 5-10 minutes or less. They could be refreshed or if using OAuth, simply redirect an unauthenticated user and they’ll be reissued based on the cookies stored by the OAuth provider… assuming the redirect doesn’t break your app of course.

Alternatives? First, you could live with the possibility that tokens are valid for some period of time after logout because it usually doesn’t matter - generally you delete the cookie and the user is logged out, even if technically they could restore the cookie later. They won’t, unless you’re under attack.

Other alternatives: You could use Redis for session data as pointed out with the random key in a cookie and delete the session when done to invalidate the cookie.

You could also use Redis to keep token IDs that you want to invalidate or block. You could use something like Open Policy Agent to distribute a list of invalidated tokens to each server.

Finally, you could send your JWTs to a centralized authentication service — single point of failure, yes, but you could record invalidated tokens to memory and responses are very quick and easy to audit. With careful planning you could reduce the risks in having a single central service to validate issued tokens.

I’m sure there are other ways to mitigate this risk. The general reason why folks don’t recommend JWTs is because it’s too easy to make mistakes in the validation logic. But the same is true (with different possible mistakes) when you roll your own session cookies. At a certain point I think you have to either assume competence or you have to suggest that developers use identity proxies in the cloud, or frameworks others have written, and never implement this themselves.

But yes, this is a rather transparent advertisement for Redis as a KV session store.


> You could have short-lived JWT tokens so it wouldn’t matter because they only last 5-10 minutes or less.

The author argues that you can't revoke a JWT - suggesting that 30min is the usual default. To their point, if 30min is too long, then 10min is probably still too long.

However, implying that 30min is too long for a token to remain stale suggests that you aren't really working at the scale that JWT was destined to address (Facebook, Twitter, etc.). If you really are the unfortunate soul trying to solve per-request authz, specifically at the scale that JWT was designed for, then I feel deeply sorry for you. For the 99%, i.e. the rest of us, 30min is just fine.

> You could use Redis for session data as pointed out with the random key in a cookie and delete the session when done to invalidate the cookie.

I'm not picking at your argument, honestly, I'm just pointing out the absurdity of authz without JWT (or similar) at scale. Redis is eventually consistent: what happens during a network partition?

Saying that the JWT expiry is a vulnerability is tilting at windmills.

> The general reason why folks don’t recommend JWTs is because it’s too easy to make mistakes in the validation logic.

Bravo, that's the real problem with JWT right there: it's too easy to misuse - especially when convenience or demanding customers enter the picture. It also has brain-dead specification opportunities like signature-free tokens.


Could you expand on why 30min session validity after explicit logout is okay? I don’t mean to sound accusatory; I would like to understand your reasoning.


If you have an attacker that can obtain the token within 30min, it is reasonable to assume they might obtain the token immediately, and use it immediately too.

JWT expiration protects against situations where the token is stored (or made to be stored) somewhere improper and later used, not being pilfered during proper use.

As the article argues, it doesn't even protect against a malicious user using stale credentials to wreak havoc, such as a disgruntled employee that had access to the precious admin panel being fired.


If the JWT is saved as a cookie, you can delete the cookie and the user’s browser is safely logged out. The threat model is that a user or third-party could intercept and reuse the JWT after logout. Sure. But then a malicious actor could re-use a JWT before you logout from the app also, which is a much larger risk. Malicious browser extensions for example could hide that they’re making clicks or taking actions in tabs just as they hide ads from you. Don’t get me wrong, extensions are sandboxed, but… any sandbox can be broken. In the end, whether or not your JWT was revoked at logout doesn’t affect the risk of malicious activity all that much as long as cookies behave the way they should. And as long as your JWT has appropriate expiry timestamps.


I absolutely agree. That’s why I said:

> you could live with the possibility that tokens are valid for some period of time after logout because it usually doesn’t matter - generally you delete the cookie and the user is logged out, even if technically they could restore the cookie later. They won’t, unless you’re under attack.


> The general reason why folks don’t recommend JWTs is because it’s too easy to make mistakes in the validation logic.

That isn't why I don't recommend JWTs, and it's not why this article is not recommending JWTs.

> Logout doesn’t really log you out!

> Blocking users doesn’t immediately block them.

> Could have stale data

^ JWTs fundamentally are not compatible with server-side authentication revocation.

Your typical developer is like, "Well, I don't care much, I'll use JWTs anyway", but then some poor soul has to deal with the ticket titled "Blocked user still able to access service", or "User still able to access service after logout", or "session never expires!" (oo... look, you configured it incorrectly).

I've been on the other side of having to deal with screwed up JWT implementations other folk have built.

It's not fun.

...and yes yes, there are work arounds, you can have rotating short lived tokens and long lived refresh tokens and a database of revoked tokens...but you've just reimplemented server side session authentication yourself, and it's probably wrong.

> I think you have to either assume competence...

I think that's terrible advice. You should never assume developers are competent to implement authentication. It's a Hard Problem, like, writing a database, and frankly, only specialists are competent to do this correctly. Anyone can writing some kind of data store, but it's a bit harder having it ACID and concurrently multiuser.

> or you have to suggest that developers use identity proxies...

...but yes, this is probably the only useful piece of advice to give to people: If you use a 3rd party authentication provider, you can delegate responsibility for doing the hard work of making sure they have a solution for the hard things; at which point, you don't really care if its JWT or cookies, or whatever.

I still wouldn't recommend people use JWTs.


"database of revoked tokens...but you've just reimplemented server side session authentication"

There's one important difference between session authentication and JWT+revocation: the revocation list can be distributed asynchronously, whereas a session list needs at least read-after-write consistency.

It's a minor point for most use cases, but occasionally can be very significant.


That said, the primary problem with distributed authentication and distributed revocation is either it slows down your processing as you wait for confirmation of new entries on the list, or you have a risk that actions might be allowed when another part of the system has tried to block them. We generally think of distributing authorization data asynchronously in the positive: adding a new user permission, rolling the list out to every authz server. But when you’re trying to block access immediately, it can be difficult.

Not to be negative—if your JWT expires after 15 minutes and your list updates every 10 seconds, you’ve improved your reaction time quite a bit for revocations.

Also, you can have an eventually consistent session store if you don’t mind that sometimes users might see a 403 they shouldn’t, if your app can cover it up such as by requesting content from a different region or waiting a bit. Similar to the idea of using a load balancer to pin sessions to particular servers.


I suppose if you really want to avoid the hop to session storage as much as possible, you could have your applications servers continuously refresh a lightweight Bloom filter or other probabilistic data structure storing maybe-invalidated sessions in memory, and in case of a potential hit, confirm it with the session store. Then your logout endpoint just needs to wait until all servers receive the data to tell you you're logged out (I suppose CAP applies here). If this sounds like overkill to anyone reading this, then it probably is!


I do want to follow up - there are two parts of JWT issuing that are problems and why I prefer using JWTs only issued by OAuth providers (third-parties that know what they’re doing) - 1. JWT tokens you issue yourself might not have expiry dates in them - I am assuming that your JWT tokens are valid for a reasonable duration such as 2 hours or less, otherwise you should probably use a different technology - and 2. Like SSL, you will have to rotate the credentials used to sign the JWT, otherwise anyone could pretend to be anyone else, at any time.

So it’s not that JWTs don’t have risks but the risks are overstated. It would be like saying never use Redis because by default it doesn’t have secure SSL or a proper password system and thus anyone could access it. Security isn’t easy, but it can be done, and often looks like a series of mitigations, monitoring tools and trade offs… such as how long sessions last, or planning for features like key rotation or session revocation in advance…


Don’t be so sure about the third-party auth providers. Even Auth0 accidentally allowed token validation bypass by specifying a weird capitalization of “none” for the algorithm. https://insomniasec.com/blog/auth0-jwt-validation-bypass


Aren’t JWT tokens only supposed to be good for 10-15 minutes? I know using flask-jwt you have to go out of your way to make them last longer than that and it isn’t recommended.


Yes but a lot of times people don't do that properly, some frameworks have incorrect defaults or people don't want to deal with writing the logic to handle refreshing tokens after that 10-15 minute window etc etc.

They are insecure but it's not a problem if you follow best practices like those, the difficulty is a lot of people don't because they don't really understand what it is they're doing or potentially causing by making the changes that let them be lazy or do things the easy way.

You could probably change the title to articles like this from "JWT Tokens are NOT safe" to "JWT Tokens are NOT safe when you ignore all security practices and take the easy way out" but that doesn't make for a flashy title.


I know some folks who thought they could use JWTs from a traditional LDAP identity vendor (open source Active Directory so you can manage computer login credentials centrally, issue Kerberos tokens, etc.) but they managed to misconfigure their JWTs by following the defaults from the vendor so the JWTs issued were valid forever. When I tried to explain that they couldn’t be revoked without key rotation, I got some very blank looks. I’m not even sure the tokens had an iat (issued at timestamp) though if they did you could use that to at least ignore tokens older than a certain date. They assumed because they deleted the cookie at logout, it wouldn’t matter that the session could be re-used forever. Then they implemented session limits by setting a cookie expiry time. /face-palm

Issuing your own JWTs also means you have to keep the secret or certificate used to sign them secure or if it leaks anyone could impersonate anyone else. This is especially problematic if admin or role assignment is embedded in the JWT.


Expiry is just one of the fields you can set. Also if you’re curious about what’s inside a JWT, the next time you see one from an OAuth cookie perhaps, copy and paste it into a tool like https://JWT.ms run by Microsoft and it can show you the details. Commonly you want to load a JWKS (with multiple keys possible for easy revocation) to validate the signature, and check the iss (issuer of the JWT), aud (your app registration or audience of the token) and expiry date of the token. Oh and don’t forget to hard-code or reference the JWKS validation method so you don’t accidentally allow “none”. JWTs are not without their complexity, but the same could be said about maintaining secure session cookies and implementing 2FA yourself perhaps.


There's "JWT hate" articles on HN because people keep implementing JWT auth without understanding it - they want an easy, cheap way to do auth when talking to different services, but it can be a huge security flaw since you can't do revocation. There's a lot of ways to configure JWT, so it's very easy to shoot yourself in the foot.

JWT for short-lived tokens is fine - it can work well for signing requests between microservices. If you want to give them to end users, use refresh tokens.

As with anything, the alternative depends on your needs and use case. There is no universal solution.


+1


Refresh tokens are the real alternative, IMO.

I kinda agree it looks like an ad for redis, since it doesn't even considers alternatives.


Hasura [0] has a great article on how to make front end authentication as secure as possible.

[0] - https://hasura.io/blog/best-practices-of-using-jwt-with-grap...


Agreed. Long(er) lived refresh tokens, and then having signed access tokens such as JWTs so that the API server doesn't have to hit the database on every request.


No solution is universal. That said: use sessions. They’ve worked fine for decades and there’s a 99% chance they work fine for your app.


Definitely use cookies. Opaque tokens. Works great.

All the good versions involve cookies somewhere - they’re the most resistant to all the various forms of attackers on the web.

Many use cookies in order to stamp short lived tokens, though.


There’s no reason to put all the user info in redis and this article makes no valid argument for it. Give the token a unique ID and store that ID in redis (or some other fast store). Validate the token by checking its sig and verifying that its ID is in redis. Store all the signed metadata you want on the client, revoke it by removing its ID from redis. Addresses all cases where the token is stale. So basically redis becomes your token whitelist, not your store of user metadata. Problem solved.

Signed blobs of data such as certificates and web tokens are a very powerful massively distributed cache where the entity that benefits from the data being cached is also the entity responsible for persisting it. This is a wonderful optimization whose sole drawback is that you need an external way to decide when that entry is invalid. Solve that problem, don’t abandon the entire concept of the distributed cache.


It is funny that this is the way most web auth systems worked back before the JWT brand was invented. I made one that had a prefix extension bug, but you didn’t see the marketing drumbeat against it until it got a name and you could paint a target on its back.

(E.g. branding something is often a transition from people using language as a club against hunger, wild animals and the unknown to using it as a club against the other man.)

It is not unusual for systems like this to have some details such as verifying the cookie against the db if somebody is doing a critical operation and not being too worried about a five minute window for people reading articles and such.

High volume attacks need their own countermeasures.



Simple session tokens are fine, always have been.


TLS client certs?


I actually find this post/ad for Redis pretty ironic, because Redis is actually a really great solution for storing revoked tokens (since you can just store the token with an expireat equal to the token's expiry timestamp). I do think it's a serious issue that most jwt howtos don't mention expiring tokens and/or refresh tokens, but "JWTs are not safe" is hilariously hyperbolic.


TDLR; Use Redis to lookup your user data in realtime, instead of signing a token and trusting it.


+1. Redis is a great option, you can use other similar services, but just dont use JWT for something that it's not meant for.


I once watched a presentation about Macarons [https://en.wikipedia.org/wiki/Macaroons_(computer_science)] that is purportedly better than JWT. Are there any good success stories about using this?


    - Holder of macaroon can issue a sub-macaroon with smaller power, while JWT is fixed
    - Macaroon is notably longer than JWT
    - Macaroon is equivalent to signed JWT, but does not offer equivalent to encrypted JWT
Doesn't sound like it solves the problems mentioned in this article.


Not bad for an advert. If you are interested, here is a short guide how to actually implement JWT securely:

- Make the JWT short lived ( 5 minutes)

- Re-freshing the JWT should involve server state (DB hit every 5 minutes, not every request)

- Protect highly-sensitive actions (typically overwrite/delete, but not read / non-overwrite) by requiring a fresh token.


I prefer traditional sessions, but for some systems implement JWTs as well. I'd offer the above but with a couple of slight differences.

- Issue tokens containing a flag which makes them read-only and give them a short life-time (around a minute). At times of heavy use you're now performing authentication much less frequently in comparison to every request, so less scale is needed. If the token is intercepted it remains a read-only one tied to the context/permissions of the user it was provided to.

- When a destructive action is requested (write, delete) if you receive a read-only token reject it. The app will then be forced to re-authenticate to get a write-capable token, which has a lifetime measured in seconds. This covers you for a couple of quick requests and so still reduces load - but the lifetime is very fleeting so revocations, if still needed, are extremely short-lived before the token is expired anyway.

If you still want to maintain a revocation list you can stick the tokens in something like Redis with an auto-expiry set to match the token. The short lifespan will keep the revocation list comparatively small and Redis being in memory will make checking it fast.

However as the read-only token is only valid for a minute and the write-capable token for a few seconds, there is usually no need to keep a revocation list at all.

This all may not seem worth it for the small usage windows you get with a short-lived token, but if your client app/site is refreshing pages or pulling in data via 10 requests in a 2 second window (not uncommon) then you've eliminated 9 authentication requests. At small volumes it doesn't matter, and at large volumes you may reduce the authentication cycles by 90% or more. Again, without needing a revocation list.

All that said, use traditional cookies/sessions unless you have complex requirements.


Once you reach this level of enlightenment ("any session should be revokable and fail closed"), there are still higher levels of abuse you'll need to prevent.

Let's say you keep your user sessions in Redis and replicate active-active between multiple regions. Your service allows for more than one active session per user, and has a second index of such "live sessions" so that users can terminate all of their sessions at once (eg. "Log out all of my devices").

You use optimistic concurrency to update multiple indices and pieces of information. Session mutation events and validations can occur anywhere.

What if an attacker slowly logs in thousands of different accounts, hundreds of sessions apiece, then logs everything out in unison?

Redis is single threaded, and all of your validation reads are going to get jammed with impossible to clear logout events. Replication is going to play these events out in all of your Redis clusters, meaning each of your regions will fall over. Nothing in logged-in flow can be processed, and you're stuck.

That was fun to fix.

I've got so many horror stories... Redis is great, but engineering at scale is tough even with great tools.


JWT, while having shortcomings have the advantage of decoupling systems by enabling token validation and user data retrieval without having to communicate with the auth service or the database directly. Worst case, with the approach from the article, every component has to make a call to the database, becoming dependent on the data schema, etc.


If an application needs a total order over authentication,authorization, and transactional events then redis doesn't cut it either. Take this scenario:

1. User A creates resource X

2. User A sets X=1

3. User B fails to set X=5

4. User A transfers ownership of X to B

5. User A fails to set X=4

4. User B sets X=2

This scenario happens in all sorts of applications; games, document editors, financial ledgers, forums, etc. Ownership transfer is the hardest use case to support with time-limited tokens like JWT, but even cached sessions or external security services may not be sufficient because proper ordering requires ACID compliance between security and data storage. Failing to provide a total order over auth* and modification events leads to race conditions and bugs.

Ownership transfer is mostly a superset of resource deletion (how long the actual resources persist in storage varies) so if an application accepts an authorization/authentication token as evidence that a resource (including a user account) still exists it can lead to reference-after-free or stale reference bugs, especially in systems where resource identifiers can be reused, or inconsistencies and race conditions in logic that makes decisions based on assumption of event ordering, e.g. orphaning related objects that would normally be deleted when the parent/child is deleted but which fails if the owning user no longer exists, despite the authentication layer allowing the initial deletion to occur due to stale credentials.

If correctness is the goal then authorization decisions need to be made in the same transaction as the action being authorized, and authentication changes need to at least occur no later than corresponding authorization changes, e.g. revoking access before deleting a user or transferring ownership or invalidating sessions/cookies/JWTs that can't be updated in the same transaction.


It's an ad for Redis.

If you're spending too much time looking up the credentials associated with a session cookie, you're doing something wrong. Maybe you need a cache. Sure, sometimes it won't be in cache, but it probably will be.


The cache can be redis.


These articles are uniformly terrible. They all and this one complain about stateless authentication systems and describe stateful systems as better. Probably, at small scale. Choose stateless systems for greater scale and reliability when you have to and when you have the capacity to make the careful choices and compromises that come with it.

But JWTs are neither here nor there. JWTs are stateless if you put the user information in them, and stateful if you put the state lookup token in them. JWTs are a format, not an authentication system.

Never ceases to amaze me when marketing content makes me think less of a company.


This is just over stated. Expiring tokens remotely isn’t that hard.


I don't think these arguments hold up:

> 1. If someone gets access to your token [after you're logged out] they can continue to access it until it expires.

The author goes on to say that "security is binary — either it’s secure or it’s not". So... Is the token secure, or is it not?

If we can't assume that we have the capability to transmit and securely clear a string from the client, the whole discussion becomes somewhat pointless because both methods of authentication are borked.

> 2. Blocking users doesn’t immediately block them.

If you want to block a user, why would you block the JWT? Block the userId, or some other identifier that isn't coupled to a transient authentication mechanism.

> 3. Could have stale data. Imagine the user is an admin and got demoted to a regular user with fewer permissions. . .

This example demonstrates that this would not be good information to store in a JWT and blindly trust. Store a user identifier (or another immutable and unique identifier) and look up their permissions with that.

> 4. JWT’s are often not encrypted so anyone able to perform a man-in-the-middle attack and sniff the JWT now has your authentication credentials.

Encrypting the token doesn't make it harder or easier to sniff a token or to re-use it. Assuming the author isn't suggesting that people are transmitting literal account credentials unencrypted, this is the same point as #1 above because encryption and signing are meant to prevent against other attacks.


Looks like Redis is trying to promote itself as a session data storage. From my perspective:

1. There's no difference between stealing jwt or session_id with al the problems it brings

2. Session in external storage is a solution for small projects like personal blog etc. because you have almost no traffic to handle. FOr example: 1kk session with an average size 1k = 1kk * 1k = 1Gb in memory storage. Not so big but you will meet all the issues of 10k connections problems when trying to build a service witch can handle this also it will consume at least 100Mb/s bandwidth just for getting/setting data.

3. Server side session a simple place to store all unnecessary/secure data and also can grow exponentially. I saw plenty projects when in one moment session data was grown over 1Mb. So the limits of JWT is a huge plus

4. JWT is easily scalable solution. It works great with 1k session and with 1kk - 100kk - 1000kk sessions.

5. Migration. Because of limitation of server side session - plenty projects started with DB as session store just because they already have DB. It's way of pain of migration from DB to external storage memcache/redis then to JWT.

From my point I don't see any reason to keep using server session instead of JWT.


6. Microservices. It's not unusual to have several Microservices consume a common entity, like say, User. If every microservice has to authorize each request doing a db lookup, it doesn't scale either.

He does bring fair points that it's important to understand before using JWT. If your use case includes immediate logout, or to avoid stale data, then it might better to not use JWT.


There are viable solutions to the JWT revocation problem:

1. Make JWTs short-lived (10 mins or less), and use refresh tokens.

2. Note that for many apps, especially native mobile apps, a user rarely, of ever, logs out. And you only need to keep a revocation list around as long as it takes a token to expire, so your revocation list is often VERY small and thus can be kept in memory.

3. Thus, when a user logs out, they make a call to your server, and you store the user ID and timestamp - any JWTs issued before that time are considered invalid.

4. Write this data to postgres in a simple table. Then, using postgres NOTIFY functionality to update all your servers with the in-memory revocation list.

5. When checking a JWT, you validate the signature and check it against the revocation list.

This won't work in all cases if for some reason your revocation list is too big to fit in memory, but even then it's easy to extend it to a backing store if you outgrow this solution.


Default rails cookie-based session storage is similarly stateless on the server, it's just a cryptographically signed packet sent by the client. Does it suffer from the same problems? Are these problems inherent to server-stateless session solutions, is the argument that you need server state?

I think.. not actually. If you store the user_id in Rails session, as is typical.

The OP seems to be complaining about storing a serialization of the user, or the complete list of user auth entitlements, in the session, to avoid doing a user lookup at all... as is apparently common with JWT? I don't use JWT. Can you just store the user-id in JWT instead, planning on doing a db lookup for the user? Is this not something people do?

Or maybe it would apply anyway, becuase you still can't "revoke" a session? It's still true that if someone snooped on your session, they could immpersonate you I think, which is also what they're complaining about? I'm a bit confused by the threat model honestly, it's a pretty verbose post.

> JWT’s are often not encrypted so anyone able to perform a man-in-the-middle attack and sniff the JWT now has your authentication credentials.

Wait.. is he saying literally the password (that can be used to get a new JWT?) is put in the JWT payload, unencrypted? This is a thing people do, really??? It is obviously a bad idea, yeah. Back to Rails, Rails started encrypting as well as signing session cookies a while ago, because of Security, yeah.

The OP seems to be conflating a bunch of differnet things... which is fine if they all apply to standard commonplace uses of JWT I guess, or if JWT is too flexible and allows people to use it wrong... but doesn't make it very clear what the problems actually are.

Still back to wondering if the claim is that ALL no-server-state sessions are bad, or just that JWT is a bad implementation of it, or just that JWT as commonly used isn't being used right (like you COULD encyrpt JWT payload, but most people don't?)

And... it ends up just being a redis ad, really?


I flagged this post for the following reason:

Please don't use HN primarily for promotion. It's ok to post your own stuff occasionally, but the primary use of the site should be for curiosity.

The user's submission history is almost completely RedisLabs content.


I post all sorts of stuff if you look into the complete history. The blog legitimately identifies and highlights the issues that a ton of security experts have already identified for years and written about. May I ask you to un-flag it?


I did not look past your last 15 submissions, so I did not see that.

I unflagged, but it still appears flagged. Perhaps somebody else has flagged it.


> If you think about it, these constant debates themselves should be a red flag because you should never see such debates, especially in the security realm. Security should be binary. Either technology is secure or it’s not.

Hm. I'm not sure this actually matches what I've learned reading from security experts....

What do other readers think?

I am sympathetic to the idea that it's suspicious that there are so many "debates" about a security topic. But I don't think security is actually all-or-nothing/binary. It depends on threat models, risk tolerance, budget, etc.


Anecdotally I see the source of security issues with auth[n|z] and/or JWT in general as lack of developer education or miseducation due to pervasive misinformation that so many (inexperienced)? coders post and share on YouTube, Reddit, and blogging sites.

The top examples of misconceptions I see being spread:

- JWTs are encrypted or secure in-and-of-themselves. You CAN encrypt JWTs, though I don’t think I’ve ever seen this, and generally speaking they won’t be encrypted, just signed. The best practice is to not store private information in a JWT. I’ve seen suggestions to include PII in JWTs since “cryptography” is used - incorrectly thinking this means encryption or not knowing the distinction between encrypting and signing

- Base64 encoding as encryption - Posts/videos explaining the HTTP Authorization header is “encrypted” using Base64. I have no idea where this comes from other than complete ignorance

- OAuth2 - I see lots of click-baity titles targeting beginner coders using variations of “How To Authenticate Your Users with [O]?Auth and JWT”, and the post/tutorial/video goes on to regularly conflate authentication (OIDC) and authorization (OAuth), and conflating use cases or distinction between sessions, cookies, JWTs

I am sorry for nit-picking, but the author says:

>In many complex real-world apps, you may need to store a ton of different information. And storing it in the JWT tokens could exceed the allowed URL length or cookie lengths causing problems.

I am not sure I understand, is the author suggesting that JWTs are embedded in the URL? I’ve seen codes that are exchanged for a token embedded in URLs, but this one is new to me. I’ve only ever seen or used a JWT within the HTTP Auth header (eg Bearer <token>).

Additionally, the author says:

>It’s been found that many libraries that implement JWT have had many security issues over the years and even the spec itself had security issues. Even Auth0 itself, who promotes JWT got hit with an issue.

Uh, ok, I guess since software or specifications can sometimes have a bug, they can’t ever be patched or revised with a new version, so we shouldn’t ever bother using computers… seriously, what kind of argument is this?


Using JWT in the browser id an anti-pattern for a different reason: they can easily balloon over 4kB if you have groups as claims and now you got to split them and a bunch of other things that are annoying.

On the backend you get similar issues. Many servers default to 4kB header sizes, or 8kB, or unlimited, or anything between there. So now you get to have some fun configuring reverse proxies and node and uwsgi and tomcat and…


Shitty article by a shitty user with a shitty submission history. How has it lived for so long on the front page? Who upvotes this?


In my mind there is always the "red light" recently when I see big tech companies posting "technical blog posts" that are just cloaked marketing stories that pitch their product. The article felt the same and my feeling was confirmed at the end. JWT is unsafe! Use Redis!

So it is good to see that is article already has been flagged here.


I'm wondering if we can use Pub/Sub to push revocation data to all servers. Presumably revocations are rare, plus we only need to store them only for the JWT validity period, so additional memory usage should be minimal.

The downside is it looks more brittle than the simpler approaches. Upside is performance plus ability to revoke tokens.


> Note that the lightning emoji indicates a blazing fast speed. And the snail emoji indicates slow speed.

This article was embarrassing, and makes Redis Labs look childish and inept. Whatever process resulted in this blog post should be axed immediately, and this author should not be writing anything public facing.


The article references 39 minutes as the upper end of JWT token expiration.

Is this typically the case? Or can JWT token remain valid for hours/days?

(Issue being that even if you “logout” of a JWT session, the session token server-side is not invalidated as it would in a traditional (I.e. redis-based cookie/session store))


> Imagine you logged out from Twitter after tweeting.

I don't use Twitter, but that doesn't sound like the usual use case. If the device you're using is trusted, why log out? If it isn't trusted, why enter your credentials at all?


I don't know about Twitter in particular, but it's fairly common for users to log onto accounts for various services on borrowed devices and then log out when they're done. So the device is only partially trusted, or only trusted for a limited amount of time.

If you want to say you'd never enter any credentials on any device that wasn't fully trusted, well 1. That just doesn't match how many people use accounts and devices, if you want a widely used service, you must account for it even if you'd never do it, and 2. It will still cause problems if you ever sell a device, or have to return a corporate/managed device after leaving a job, or return a device for repair, etc.


https://redislabs.com/blog/json-web-tokens-jwt-are-dangerous...

> 1. Logout doesn’t really log you out!

That is true, but if someone intercepts a token then they can already see all the data (or perform all the actions), even before the user logs out, so this doesn't count as a flaw.

> 2. Blocking users doesn’t immediately block them.

Lame. A simple blacklist would suffice. The blacklist entries would expire after the lifetime of a generic token. Since the blacklist is small, it could fit in server's RAM and be fast. Or it could be stored somewhere in Redis.

> 3. Could have stale data. > Imagine the user is an admin and got demoted to a regular user with fewer permissions.

Lame. Just block the old token on any privilige changes. All roles are stored in a token anyway, so if a user's priviliges change, then all the user's tokens should be included in the blacklist.

> 4. JWT’s are often not encrypted so anyone able to perform a man-in-the-middle attack and sniff the JWT now has your authentication credentials.

Not even true. There're no credentials in the token. There's just a user ID and that's it.

> It’s been found that many libraries that implement JWT have had many security issues over the years.

And Linux kernel has had many bugs. So what now? Not use Linux?

> In many complex real-world apps, you may need to store a ton of different information.

And storing it in the JWT tokens could exceed the allowed URL length or cookie lengths causing problems. Lame. No one uses cookies or URL for that. No one stores enormous amounts of data in a JWT.

> In many real-world apps, servers have to maintain the user’s IP and track APIs for rate-limiting and IP-whitelisting.

Not a valid argument. Those apps aren't even many, they're a very small portion. Very small, almost zero.

> One popular solution is to store a list of “revoked tokens” in a database and check it for every call. And if the token is part of that revoked list, then block the user from taking the next action. But then now you are making that extra call to the DB to check if the token is revoked and so deceives the purpose of JWT altogether.

They aren't very smart, are they? Because they don't even see a difference between a short in-memory list and a database full of users records.

> Bottom line

Bottom their ass


None of your security postures will fly at any organization with an infosec team.

AuthN/AuthZ is one of the most important areas to lock down.


So you don't have to say anything on point. Just abstract imaginary accusations. Move along.


I worked for two years on a session system for an institution that cared deeply about security.

We had to trace, kill, kill all for single or multiple sessions, users, or organizations. Do this for entire IP ranges, client versions, or client ids. Rate limit all "session-mutating" (unidirectional state machine) actions, time out, model session confidence, restrict session scopes (subset authz), associate with any entity written or touched, monitor lists of compromised emails and passwords, require 2fa...

Your opinions might work where money and privacy aren't involved. But I wouldn't trust your posture with my bank info, emails, or personal messages.

What happens if your signing key leaks? Or you accidentally log session JWTs? Or someone's device goes missing, or someone steals a password, or an account manager has their info posted openly?

We're not worried about just our entire database. We're worried about any ATO, undesired action, or data leak, however small. Even one impacted person is too many.

Security really fucking matters. Take it seriously or don't touch it.


I don't get why you couldn't have a panic bit set the same way you maintain an in memory revocation leak... Also I don't get how this is any different than "what if a DB password" leaks?

What if someone steals a device and copies the httpOnly cookies from the network developer inspector tab then forges the requests using Curl?

I don't see how these problems are unique to JWTs and how they exist unmitigated with the use of in-memory revocation list.


Every time I start a new job I feel like I spend a lot of time trying to argue this point exactly. Don't send a JWT to the client, just send a fucking cookie.


JWT is fine when used within an auth protocol like OIDC. It’s like claiming certificates are unsafe because I send the public and private keys.


Question: do these arguments not apply to any certificate authority system? Token (cert private key) lifetime, revocation lists, stale data?


This reads as redis marketing content push, probably coinciding with their upcoming IPO.


TL;DR as always, is that there's nothing wrong with JWT. The problem is with thinking that there is a way to have an authentication token that isn't persisted in any way, so long as you want the ability to invalidate a token (i.e. logout, user banned, password change, etc).


The difference tho is maintaining a list of user ids you dont accept, a "blackist" (likely very small and each record expires as the token expires). This can be kept in-memory

Vs doing a network request to your DB and searching a table that could have millions of rows.


Well this is what I mean by "persisted in any way." A blacklist is still persistence.


Rightly said.


maybe you can add a `sid` in jwt token, then store this sid in redis. when you want invoke a session, just del this sid in redis.


The author mentions that "One popular solution is to store a list of “revoked tokens” in a database and check it for every call. And if the token is part of that revoked list, then block the user from taking the next action. But then now you are making that extra call to the DB to check if the token is revoked and so deceives the purpose of JWT altogether."

I believe the author is 'assuming' the devs are not using a db to validate revoked sessions.


It doesn't have to be a DB tho it could be a server in-memory list that gets updated via pubsub or some other means. This is a hell of a lot faster than doing a network call to a DB and because revocation lists should be small by definition and only exist for the length of the token expiration they are limited in their space requirement




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: