Hacker News new | past | comments | ask | show | jobs | submit login

Top reason for me always was :

> You cannot invalidate individual JWT tokens And there are more security problems. Unlike sessions - which can be invalidated by the server whenever it feels like it - individual stateless JWT tokens cannot be invalidated. By design, they will be valid until they expire, no matter what happens. This means that you cannot, for example, invalidate the session of an attacker after detecting a compromise. You also cannot invalidate old sessions when a user changes their password.

> You are essentially powerless, and cannot 'kill' a session without building complex (and stateful!) infrastructure to explicitly detect and reject them, defeating the entire point of using stateless JWT tokens to begin with.




This may not be part of the spec per se but you can invalidate them by distributing a bloom filter with revoked tokens and validation times, services just need to poll the service at the granularity needed. This makes scaling still much simpler than one big session store.


The problem, as the linked 'Slightly Sarcastic Flowchart' says, is: how do you handle the invalidation server going down? If you just assume that tokens are valid in this case, then an attacker just has to kill the server and they're back to being impossible to invalidate. If you assume they're invalid, you're back to having centralised state, which mostly defeats the purpose.


The bloom filter allows you to largely distribute the work of the server, and serves as a reasonable proxy during a failure off the server. More importantly though, there's no reason it need be a centralized server. Invalidated tokens could be broadcast to a wide number of servers that each maintain the invalidated token list (it's a great case for a CRDT, since it is append only with a TTL). Normally it'd be a pretty compact list, and if it isn't, you probably want to take a more defensive posture anyway.

...and beyond that, if an attacker can take out your invalidation server, which needn't be directly accessible to the public, you've already had a pretty serious security breech. I think the least of your problems would be the invalidation server.


Thank you for this. While I love a good old nerd round of 'stump the wizard' I also appreciate someone pumping the breaks with some realism.

Some security conversations get to the point where it's stringing together a highly unlikely chain of scenarios requiring multiple pivots, multiple concurrent failures (stochastic or otherwise), and a state sponsored actor in order to slap a 'do not use' recommendation on something.

If companies like Nike can run _Magento_ and educational sites still require Flash then I can use JWTs. Maybe...


It might even be a real use case for blockchain!


It’s largely irrelevant because the revocation bloom filters are cached on each service, and if the auth service is down then tokens can’t be revoked anyway so the list is still accurate enough.

TBH I don’t think the author of the article has expirenced the nightmare that is a hot session store at a large scale before, you end up with needing to troubleshoot IO latency issues with basically no tooling that can show you where the problem is and you’re up against the hardware limits and what ever black box your cloud provider has made. Where as with JWT everything happens in normal user space and can be reasonably reasoned about with a bit of complexity without razor thin latency deps on IO performance.


I'm confused by your comment. Session stores seem like the easiest storage to scale horizontally. The workload is just a distributed hash, _maybe_ with atomic updates. "Three nines" durability over one day is perfectly acceptable. Use a consistent hash ring, no replication, and add nodes until you achieve the performance required.


It’s easier if you have one lookup key for a session, but often that’s not sufficient, consider the case of termination of a session by email address or by a session id. The other issue is you still have every service making high rates of network calls that require low latency since they’re often inline with the UX. So you’re relying more on a chain of IO between services which is nearly impossible to performance troubleshoot with today’s tooling.


> > You are essentially powerless, and cannot 'kill' a session without building complex (and stateful!) infrastructure to explicitly detect and reject them, defeating the entire point of using stateless JWT tokens to begin with.

I'mnot sure that's entirely true. It's not hard to include an extra static token within the JWT which is delivered to the servers through an alternative method (pushed with production, or manually set), which when it changes forces a re-auth (either just a DB auth session because you've specifically set the conditions such that you only care if PW changed or some other flag set, or a full login procedure).

If the same static token is used for all users, all users will need to do this on the next request. If you subdivide your users by some metric (first x characters of username, some modulo of numeric ID) then you can invalidate some subset of the userbase JWT tokens instead.

You've now gained the ability to immediately invalidate a JWT tokens as long as you're okay with forcing extra computation on some larger set of completely valid ones at the same time.

Edit: Changed wording of last sentence to clarify you don't necessarily have to invalidate all sessions just by invalidating all JWT tokens, as outlined in further comments below.


Well, you are adding infrastructure to explicitly reject the tokens. It is stateful infrastructure. And you can not reasonably do that if you use the tokens as session holders.

So, should I think that you agree with the article?


It is stateful, but with a minimal amount of state that is global. In that respect, it isn't "defeating the entire point of using stateless JWT tokens to begin with."

The point is not to be stateless for its own sake, but to leverage less state in a way that's beneficial (e.g. not having to authenticate every request). If adding back a little bit of state in a different way mitigates the major downsides, that can still be useful if it doesn't entirely negate the benefits already gained.


To be fair, this discussion is unhealthy being full of hyperbole of both sides. Your were one of the first answers, so there was a lot of lost context.

The article has a perfectly nice point, that nearly every site does not need stateless sessions. Keeping them on the database is perfectly fine.

The article also has a point that there it is impossible to revoke certificates without shared state.

People here have a point in that it is possible to optimize that shared state as much as it is a performance non-problem.

Other people here have a point in that this is a hard problem that isn't solved by default, so you either do some low level work creating your revocation list from basically the ground up, or you'll get something that is worse than a database.

Yet other people have a point in that it's hard to set your PKI correctly and if you don't want to bother with that, you are better with a standard database implementation.

All of those are true.


This is true, but kicking off a large subset of the userbase every time an account is compromised (or more realistically, every time someone changes their password—which should certainly invalidate their old tokens) isn't going to be a valid trade off for most applications with customers.


A 'standard' JWT claim is Issued-At ('iat'). If you want lightweight JWT's, you're going to be loading minimal user data serverside anyways (logging, roles, etc.) -- it's trivial to compare the iat timestamp to a 'last change' field in your user object.

Of course, this gets to the point of the article -- if you're loading data serverside, that's not reeeaaallly the intended multi-party-claim-exchange use case of JWT's....


You don't have to kick them off. You can make it a trigger to just check whether their session has been invalidated. Store last password change date or something similar, and if the db user is still valid and the password date is older than the JWT creation date, just update the JWT and let them continue. If not, require a new login procedure or deny outright, depending on account status.

> every time someone changes their password—which should certainly invalidate their old tokens

Using a variation of above, you can just reverify the JWT every X minutes, and know that if you change a password for normal reasons all JWT tokens will be invalidated within X minutes.

It's a trade off. If you can live with what you're trading (or at least live with it after mitigations are put in place), it might be worth it.


We tie our users session in the JWT to the session in a central database, this allows us to invalidate individual sessions.

The reason for using JWT is that the UI and backend consume the same session object seamlessly. Before what we got in our PHP session and what state we shared with the UI were manually kept in sync through a API request.


> session object

what fields are in this session object besides an identifier?


>It's a trade off

But, what are you getting for this added complexity--above and beyond what "traditional" user session management provides?


You can serve pages that don't require DB access without ever hitting the DB. Static content that is restricted to authed accounts is able to be served immediately and directly. Requests that access resources other then ones shared with the auth system do not need to first access the auth system to verify the account. Even if the auth system is on the same DB as the rest of the content, it's no longer a sequential bottleneck.


Yeah, I can see scenarios where these features would be useful, but wonder how many architectures truly benefit from it? I'd guess that a good many people implement JWT b/c it's the accepted approach or in premature anticipation of a future scaled-out architecture that would land it more in one of the beneficial scenarios.

Of course, if the DB concern is one of performance, then there's also a performance trade-off with JWT since the (sometimes large) token is constantly transferred over the wire and compute is used to decrypt it. That may be faster than a DB-round trip, but it's not free. Of course, "old-school" session-management keeps the auth state in memory server-side, which can be the fastest--though comes with a memory hit. But, constantly processing the token in-memory also assigns a memory penalty (even if slightly more transient).

In any case, I think it's like a lot of popular tech that evokes strong reactions on both sides: The success of the tech causes it to be overused, invariably in use-cases for which it isn't as well-suited or was not originally designed.

In this case, the important need for session expiration alone makes me question hard whether JWT is a good fit for session management in a lot of user-facing apps. In some cases it will be, but in others, there are probably better solutions.


Sure, I'm not making a case for using JWT, I'm just noting the options and capabilities that you have if you do, which I think weren't accurately expressed initially.

There are cases where JWT can make a lot of sense. For example, a high capacity API where you don't necessarily want to check auth for every request, especially if you can rely on some sort of caching infrastructure. In that case it may make sense to use JWT, re-check the authentication on some set schedule (whether once an hour or day), and save yourself what might be a few thousand authentications a minute.


Yeah, I always thought the API scenario was the original use case for JWT anyway and believe that it does makes more sense. I think API token expiry is a requirement too, but not as pressing or frequent as on the user-side.

With the advent of SPAs and RESTful designs, it was probably tempting to say, "hey, let's allow our user-facing client apps to hit the API directly and use the same JWT token scheme for auth there". So, whereas it was generally a good scheme for APIs, it became a YMMV thing once it diverged into the client.


If you assume that token invalidation happens infrequently it can still be easier to create a communication channel from individual clients/services to the authentication API through which they receive revocation requests/lists than to check every access token explicitly using a combination of API requests and caching (which also introduces invalidation delay). I'd say it really depends on how many requests per second you need to serve, how your architecture looks and how often you have to invalidate tokens.

I can definitely see the appeal of JWT tokens for infrastructures with many decentralized services and a very high request load, even when taking into account the added complexity due to invalidation issues.


How the heck did JWT get such a following with issues like these?


This article is propaganda. This is not the first article of its kind; they just keep popping up over and over on HN with the same poor arguments. I usually take the time to write long explanations as to why these points are invalid but I'm tired of arguing with these people.

The real reason for these articles I think is that some developers had a very traumatic experience with a poorly implemented JWT authentication system once in their career and now nothing will change their minds about JWTs.

Maybe JWTs don't make as much sense for HTTP but for WebSockets they are absolutely crucial. With WebSockets for example, you can push the JWTs to the clients so you can make them with very short expiry: e.g. even 10 minute expiry is possible... Issuing/renewing JWTs with very short expiry helps avoid the need to explicitly invalidate them... If used correctly, JWTs can take a lot of load off from the database.

I could go on for a long time about the benefits of JWTs with WebSockets but to summarize the best aspect for me is that you can push them to the client with short expiry and also that you can automatically renew/re-issue them on an interval while the connection is alive... It also works well for dealing with socket reconnects and saves database lookups.


> This article is propaganda.

Someone else called it FUD, now you're calling it propaganda. I'm seeing a pattern.

> This is not the first article of it's kind; they just keep popping up over and over on HN with the same poor arguments. I usually post long explanations as to why these points are invalid but I'm tired of arguing with these people.

...they said, commenting on an Internet board where discussions (a.k.a. arguments) often unfold with these people.

Why are these arguments poor? In what sense does "JWT instead of server-side storage" make a better engineering decision?

> The real reason for these articles I think is that some developers had a very traumatic experience with poorly implemented JWT authentication system once in their life and now nothing will convince them otherwise.

Your ad hominem straw man argument notwithstanding, there are two orthogonal arguments that JWT defenders seem to conflate:

  1. Don't misuse JWT in places it wasn't designed for. <- You are here
  2. JWT is an error-prone cryptographic design and should be replaced with a better standard.
You can agree with one without agreeing with the other.

> Maybe JWTs don't make as. much sense for HTTP but for WebSockets they are absolitely crucial.

Would PASETO be a better fit than JWT for your envisioned WebSockets use case?

https://github.com/paragonie/paseto/tree/master/docs


> I usually take the time to write long explanations as to why these points are invalid

Could you link to one of your long explanations ? I'm interested in a rebutal but understand it's annoying to repeat yourself.


Could you explain why they're important for WebSockets? Hadn't heard about them in that context before.


My guess is that a database read/write isn't necessary with JWT, and so WebSockets flying by in a scaled system aren't impeded by this extra layer of complication.


Probably just because web sockets tend to be used for frequent updates and jwt allows you to authenticate a user without necessarily pinging your sessions table on every single call.


Do you have to ping auth on every websocket communication? I feel like you should be able to auth once when you open the connection, and then keep track of the intended session timeout locally (unless you expect external factors to extend that timeout I suppose).

Unless that's in regards to checking whether an auth has been blacklisted, which isn't something jwt solves, as far as I can tell.


Having recently joined a project that uses JWT (I was completely ignorant of it beforehand - cookies all the way), I can say that a key advantage is convenience for developers and testing. Once you have the secret for signing you can pretty much access any of the application without having to jimmy the access-controls. I guess that could be a weakness too, but in my experience in fast moving commercially driven environments with little technical oversight (a lot of the industry these days) developer convenience does seem to drive a lot of design choices ...


> JWT (I was completely ignorant of it beforehand - cookies all the way)

JWTs can be stored in cookies. So I don't understand why you say this.


Oh I'm not aware of the means of using JWT you describe. I think the context of this article is using JWT exclusively, and by the looks of things plenty of people are doing just that.


The article specifically addresses the confusion that you're exhibiting:

>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.

There's no such thing as "JWT exclusively". The JWT is just a chunk of data; it has to be stored somewhere, either in a cookie or in local storage.


I’m not too sure what you’re talking about but the article is about this confusion.


JWTs have long been a favourite topic of software engineer bloggers as they do raise a lot of interesting technical questions.

Whether we can take this as evidence of a big following in actual production implementations I’m not so sure, but if forced to guess I suspect they are exceedingly rare (compared to stateful systems) outside of programmer side projects.

I’ve rarely ever seen stateless auth used on anything serious, as others have noted many times before the issues with invalidating previously issued tokens often removes the “stateless” part, after which you are just as well using more traditional mechanisms.


The article exaggerates these issues. Also there are easy solutions available for those concerns.

It's trivial to store a token version on your user object and invalidate any token that has the wrong version.


Then just store session ID there and get rid of JWT.


There's big difference between fetching session from db and validating id via short local (push distributed) revoke list. Well, that's if project is large enough to saturate storage :)


If you don't care about invalidating single user sessions or any of the other downsides, it works pretty well.


Because front-end engineers always fall for whatever cult is hyped right now.

They usually don’t understand the issues around the things they want to build, they do it because it’s cool and new, and don’t stop to ask or research anything.


And backend engineers not researching how to properly implement them and completely missing the point, yet arguing and blaming front end devs for being "useless". /sarcasm

On a serious note: JWTs have use cases, but your company is probably not the one. Nice thing about JWT is that there is no need to query the DB every time a request hits a service. JWTs for session should be short lived and refreshed often. In case you need to invalidate it, you invalidate refresh token and window for attack becomes short. It's not perfect but it's certainly not useless and just the hype.


Short TTLs on tokens can work very well depending on what the goal is.

If you're doing high-ops and your goal is to reduce authorization calls the TTL acts effectively as a cache that decouples API ops and authorization ops.

The compromise being that you can only rotate as fast as the TTL.

That is of course unless you want to implement revocation-list capabilities. But if your revocation-list server goes down you're back to the TTL limitation.


The reality is that hardly anything needs to be instant. You're powerless in the time period that you're setting.

You don't need to have Reddit-scale traffic to have to consider this, but when you deal with a lot of backend systems it can reduce complexity a lot.


Isn't this like the one component of an application design where you really do need it to be instant? People get confused because invalidation doesn't happen often. But you also don't use a fire extinguisher often, and you get those checked all the time to... wait, bad example.


I guess that depends on the application. So long as you can guarantee that it happens within a given time frame that might very well be good enough.

That said, I don't think a lot of people really take this into consideration when they use a stateless JWTs, or the computation required to issue really short lived tokens.

Aaaand now I have to check my fire extinguisher.


> I guess that depends on the application. So long as you can guarantee that it happens within a given time frame that might very well be good enough.

I think the right answer is that you need to develop some type of threat models, review them periodically, and then decide what's right. Just because you can't think of a problem with the approach now doesn't mean it won't happen later, or be a component of a larger attack. The "given time frame" above may be more than enough for certain attacks you can identify in initial threat modeling.

From the long-term operational perspective, it may also be preferable for the application owner to have immediate invalidation. This might be inconvenient to the development team just trying to get their work done now, but is it a good idea to ignore this requirement? For various reasons, dev teams tend to push on only for there to be problems later.


Couldn't you use JWT's with both an access token and a refresh token? The access token can have a fairly short expiry time, after which you hit the auth service with the refresh token, at which point you can handle any token invalidation, while still allowing most of your requests to go through just using the JWT.


Not true. Encode a token and validate it against a constant, which you change in case of a compromise.


What's the point of having a token if you need to check it with a database on every request?

Instead of `SELECT isValid from tokens where token="<TOKEN>"`, why not `SELECT user from sessions where session="<COOKIE-SESSION>"`?


You don't store the token in the database, you put it in a channel that deploys with your app and can up updated as needed. That can be a flat file shipped with production, or if a production push is too costly, set some other way.

Changing the static token causes all JWT token verifications to fail (since the token is stored in the JWT),so requires each user re-auth on the next request. It's a manual fail-safe to invalidate stored sessions.

This, of course can spike load considerably in the period immediately after, so if that's a problem, you can always use multiple tokens based on some criteria, such as one matching the first letter of the username, or the user id mod some value, so split your user into N chunks that each have their own invalidation token, so you only have to invalidate the sessions of 1/N users.


>>Changing the static token causes all JWT token verifications to fail (since the token is stored in the JWT),so requires each user re-auth on the next request.

So if a single user's token gets compromised, you force ALL users to log in again?


No, you force the JWT to be verified against the DB, which might be as simple as checking if the JWT creation time is older than the password update time in the DB, and if so, force only those users to log in.

All the token does is force some extra level of scrutiny. It doesn't mean the JWT has to be invalidated,but it does give you the ability to selectively invalidate specific ones for the cost of extra DB load once for all affected users.


I see. Thank you for clarifying. :)

Anyway, I'm still not convinced because when it comes to security I feel like it's a better idea to walk the well-trodden path, rather than invent new and exciting mechanisms. JWTs are useful for other stuff, but using them for security requires increasing back-end complexity quite a bit.


Sure, I'm not making a case JWT should be used, just that in the cases where the benefits it provides can yield real value (e.g. high capacity secured API with good caching infrastructure behind it), there are mitigations that might help offset the downsides.


Exactly, the moment you add statefulness you've basically reinvented session authentication.


There are a lot of problems with storing cookies when the front end and back end are hosted on different domains though. That is the main reason why people use JWTs with SPA apps, because otherwise it wouldn't really be possible to serve the front end from a CDN. Most people using JWTs aren't using them because they're stateless, but rather because it's the only way to solve the problem.


Why are checking against the DB here? Parent said a constant. You'd just check the constant value within the JWT vs the constant on the backend, and update the backend constant to invalidate.


(Correct me if I misunderstand you)

If you check against a single constant in the backend, are you invalidating all JWTs for all users whenever you want to invalidate only one? Even if you keep a blacklist of invalidated JWTs, how do you keep it up to date? How do you distribute it? What happens if the blacklist becomes unavailable for whatever reasons?


With jwt you have a standarized way to get the data on the client side for example.


Doesn't the author address this in their "Why your solution doesn't work" flowchart? Everything ends up a reinvention of traditional sessions, only with a less battle-tested implementation.


So you're saying you can have sane sessions as long as you have some server-side storage? :-)


Yes, of course, jwt can facilitate that. Using it as a replacement is not good, i agree.


But what value is it adding over ordinary 'bearer token' sessions in that case?


When you aren't invalidating tokens your app server can serve all static but restricted content without checking with the DB.


Or, implement a "master token" scheme: have a long-lived master token, which is used only for generating short-lived (e.g. 5 min) tokens that are actually used for authentication/authorization at the API endpoint.

User gets banned or privileges revoked? Fine, revoke the master token on the authentication server and after 5 minutes at the latest, the user is locked out as he cannot get new valid short-lived tokens.

Sessions aren't difficult - as long as you stick to one language and environment (e.g. PHP). But as soon as you throw in for example Java to communicate with a SAP backend (as an online-shop system might need to), nodejs for a "support/sales chat" system and whatever, a JWT scheme is easier to implement.


> Fine, revoke the master token

This is exactly the ability which JWT lacks.

You can, of course, combine a session ID (checked against a DB) with a short-lived token (stateless, with expiration time encoded). This will allow to only check the session against a DB if, say, more than 30 seconds has passed since the previous check. It helps with spiky traffic, and still allows to invalidate a session soon enough for practical reasons.


And this reduces complexity of all backend systems, as they only have to deal with one mechanism for authentication and authorization.


>> Fine, revoke the master token on the authentication server and after 5 minutes at the latest

That might be fine for a lot of use cases. But anything that can steal money using that token and 5 minutes is a very long time indeed.


    But anything that can steal money using that token and 5 minutes is a very long time indeed.
Then implement a check for the token in destructive operations. Should keep the performance impact pretty low.


With this method isn't the downside that you have to invalidate all tokens, and not just the attacker's tokens?


which isn't bad because all the processing is done client side anyway.


> which isn't bad because all the processing is done client side anyway.

I believe people are downvoting you because you may have overlooked the processing that must happen on the server to encode the token in the first place.


How do you encode it in such a way that you can invalidate individual tokens? How do you distribute that information among your services?


The way we do it is with a long lived JWT (LLJWT) and short lived jwt (SLJWT). Our LLJWT lasts for a long time (think years) and is only able to to request a SLJWT, it alone has no abilities. Inside the LLJWT we store a UUID that we also store in the DB. The SLJWT is only valid for 1hr (we are testing on bringing that down to like 10min or so) and when it expires you have to use the LLJWT to request a new SLJWT. If at anytime we determine that there is abuse going on or someone wants to "remotely sign out" of a device they can invalidate the LLJWT and the next time it is used to request a new SLJWT it will be rejected.


Right, but then you just added state and re-invented sessions, in a round-about way.


True, but it was what we decided was the best way forward with web and mobile app users to standardize how we manage authentication. Does it share characteristics with sessions? Yes and we are ok with that.

Edit: One more point, we also like how JWT's are language agnostic so that we can jump between languages very easily if needed and still use the exact same concepts. Sharing sessions between, say, Java and PHP is not something that is simple/fun/easy to do. JWT's are also easier for us to reason around with, maybe it's just me or my team but I see them as more finite than sessions.


If you need to access the DB to check if you can emit a new SLJWT, what's the point of the LLJWT? Why not just store that UUID by itself?

The SLJWT does seem useful, but 1h seems too much; if I fear someone might be (for example) accessing my email account using a stolen or hijacked device, they can do plenty of damage in that time. Do you make it clear to the user that their request for a remote sign out might take that long to be applied?


> If you need to access the DB to check if you can emit a new SLJWT, what's the point of the LLJWT?

It doesn't have to hit the DB, it could hit memcache or similar caching layer. It also means we could separate this table out from our main DB. We have done neither because it hasn't been an issue (performance-wise) yet.

> Why not just store that UUID by itself?

We could do that, our thought process was that we wanted everything to be a JWT and we liked the ability to, given a JWT, know who it was issued to, when it was issues, and when it expires, all without going to the DB.

> The SLJWT does seem useful, but 1h seems too much; if I fear someone might be (for example) accessing my email account using a stolen or hijacked device, they can do plenty of damage in that time. Do you make it clear to the user that their request for a remote sign out might take that long to be applied?

Totally agree on 1hr being too long and that is why we are planning on lowering it. We have technical reasons (legacy apps that we are trying to get updated) that will not work with lower than 1 hour (I won't bore people here with the details and there are workarounds but we haven't implemented them yet)


In the past, I've compared the "iat" (issued at) value with a column in the users table called "invalidate_tokens_before". If I need to invalidate tokens for a user (for my use case, it would always be all tokens for a user at once), I just touch that timestamp column. True, it still required a db lookup (one that happens anyway), but I found that easier to manage than storing and managing session tokens.


Doesn't the author cover this in their rebuttal (http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...), "Your blacklisting/revocation server goes down, now what?".

Everything from there goes to "Congratulations! You've just re-invented sessions (only with a less battle-tested implementation) and gained nothing in the process"

(Sarcasm is from the author's post, not mine!)


I don’t think of JWT as a replacement for db lookups or storage, but they do provide a convenience in not having to store and manage all sessions in the database[1]. I’ve done it both ways, and as long as you’re careful about a few of the potential security issues with JWT (solve it once, put it in a reusable module), it actually does save a considerable amount of code and complexity on the server side.

Also, in my case, "your blacklisting/revocation server goes down" means the whole application is down anyway, so that's kind of a moot point.

You may disagree, and there are valid reasons to avoid JWT. I'm just saying that under the right circumstances, it can be useful.

[1] The author claims that JWT isn't any easier, but then later says things like "Expiration can be implemented server-side just as well, and many implementations do". That's true, but it is something extra you have to implement yourself, i.e., not easier (for that feature at least).


The author is pretty clearly comparing using jwts with using some framework provided session solution, not having you implement sessions from scratch.

So just use a session library with expiration implemented, don’t take one without and add it on yourself.


Again, I’m not arguing that traditional sessions are bad and JWT is categorically good. Of course, if everything is already implemented for you[1], then ease-of-use is less of an issue, but there are valid use cases where JWT makes sense (it isn’t categorically “bad” as the author tries to show).

For some of my particular use cases[2], I happen to prefer the JWT approach (despite all the points given in the article, and after auditing the libraries used). For others, I definitely prefer traditional session tokens.

[1] Just because it’s been implemented by someone else, the complexity of that code still falls on you - especially so with something as critical as authentication. It’s not automatically “easy” because someone else implemented it; I’ve seen some pretty terrible security issues in both JWT libraries (e.g., insecure defaults) and framework-provided session management libraries.

[2] Guess what? If you have tens or hundreds of millions of users, managing sessions and expiration can be a bit of a pain. Example: to keep things performant, you'll want to periodically clean up your session store (rather than just invalidating at lookup). Congratulations - now you have a background worker and the complexities of dealing with that at scale. Point is, even with nice framework-provided libraries, managing session tokens on the server can add considerable complexity.


> there are valid uses cases where JWT makes sense (it isn’t categorically “bad” as the author tries to show).

The author does sound negative, but doesn't claim JWTs are categorically bad; he actually mentions cases where JWTs are useful: when they are used as single-use tokens. The author claims JWTs as sessions are too problematic to be useful.

> I’ve seen some pretty terrible security issues in both JWT and framework-provided session management libraries.

It seems to me the author is arguing JWTs (used as sessions) are more error prone and less battle tested than traditional session management. So if you've seen terrible security issues...


The "battle-tested" argument is becoming less and less obvious. As for being error-prone, I've seen a lot of people set up their session cookies incorrectly/insecurely when using traditional session tokens (and framework-provided authentication libraries), so I'd guess the two approaches are about on equal ground in the "error-prone" department.


Yes, the battle-tested argument becomes moot when enough time passes and there's enough adoption. This doesn't make the original argument invalid, though -- just unheeded! ;) It seems we software devs are doomed to reinvent the wheel, again, and again, and again.

As for session cookies: like the author says, cookies are a storage medium and orthogonal to the issue. You can have all the problems of JWTs in addition to all the problems of cookies.


Exactly! Because if you're implementing everything from scratch, you'd have to check the JWT's expiration yourself... it won't automatically expire by itself otherwise.


And how is that better than using sessions in the first place? What is your gain?


In which case you wouldn't use JWT tokens at all...


Why not just "invalidate" the client? At best, you're making your application "safe" for 900ms or whatever the expiry date is.


What do you mean by "invalidate the client"?


Seriously?


I would like to know what you mean. “Invalidating the client” seems almost nonsensical since you don’t control the client.


You do control what clients (think client_id/secret) can use your APIs. Don't you? You figure it out from there.


No. The discussion here is about using JWT for sessions with web clients.

If you’re registering a dedicated client id and secret for each web client, they you’re doing something wrong. If you’re doing this and then also using JWT, then you’re doing something really bizarre, and still wrong.

Id/secret pairs can make lots of sense for integrating partner services with your API. They make no sense for web clients, where you should be authing a user and not a client.


Classic hostility on HN. I'm out, I'll go find my tribe.


Nowhere was I hostile. I answered your comments politely and clearly. You were arrogant and condescending but you apparently don't actually know what you're talking about.

Please do go find your tribe of condescending devs with fragile egos.


No, you don't control the client. The attackers do.


Yes, seriously. How to invalidate is exactly what is being discussed.


I bruteforce a password for your JWT-enabled app. Then I have a token. Copy the token from my browser ( usually just open Network tab at the inspector and copy the headers for the request ). Then I store the token at my server and make it execute a request to the app on intervals to prevent from expiring ( reissue ) the token.

How can you invalidate my server now?


Yes you can. JWTs are just a standard way to encode state, and can just as easily be sent as a cookie or the Authorization header.

How you invalidate either is the same, whether its a database lookup or something else. Article and most of the comments are wholly inaccurate.


The whole point of the "JWT for Sessions" design was to prevent server-side storage.

Otherwise, they could just use a long random string and maybe HMAC it and call it a day.


Perhaps, but what they actually are is a standard way to encode some data. Whether that's a single number or entire user session state is up to you. And how you pass that data back and forth is up to you.

There are some common usage conventions but the limits described in this article are completely arbitrary.


Nobody here was arguing about what JWT is, the article is about how they're often abused in an attempt to obviate server-side persistent data storage in pursuit of "scalability".

The title is "Stop using JWT for sessions" not "Stop using JWT".

The arguments against JWT themselves are wholly separate: https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...


The basis of that entire argument is based on a false understanding of what JWTs are and how HTTP state transfer works.

If you want to say that client sessions should not be 100% on the client side and should be split between client/server then just say that. There are pros/cons but at least that is a clear argument instead of misleading about JWTs.


> The basis of that entire argument is based on a false understanding of what JWTs are and how HTTP state transfer works.

I don't even see why you believe this to be the case. The article (and the follow-up) are both pretty clear about what the actual issue is.

Did you read them or just react to the headline?


It seems you are overly defensive about an article that you agree in other comments is "not about JWTs" even though it is titled as such and talks about them at great length.

I think you can also see from my other comments that I know what I'm talking about and my issue is the fact that * instead of discussing client/stateless vs client/server shared state*, this article spreads more misleading information about JWTs and sessions.


adding a database lookup is adding state


The point is that JWTs are irrelevant to this discussion. How you pass data between client and server can be cookies, or another header. How you encode that data can be JWTs or your own format. It is not either/or.

Whether you manage state 100% on client or share it between client and server is an entirely separate topic.


> This means that you cannot, for example, invalidate the session of an attacker after detecting a compromise. You also cannot invalidate old sessions when a user changes their password.

Sure you can. Just have a 'date_password_last_changed' field on your user model, and compare that with the issued_at date on the jwt. Since your user model presumably gets retrieved for each authenticated request anyway, there are zero extra database lookups. Similarly, if there is a global compromise then just rotate the secret key use used to signed the JWT.


Updating the 'date_password_last_changed' will invalidate all the tokens for a user. It solves the purpose but has its own trade offs.


> Updating the 'date_password_last_changed' will invalidate all the tokens for a user.

If someone is changing their password because it has potentially been compromised, what is a scenario where you would not want to invalidate all of their existing tokens?


Invalidation isn’t hard to do at all. Simply check every half an hour or so. It does slightly undermine the benefit if using jwt tokens, but it’s better than checking against a server for each request.

This is why it usually takes some websites 30-60min to completely revoke your password. This may be a concern for highly secure apps, but for most apps I think this will suffice.


Every auditor in the world checks to make sure password changes invalidate sessions, and every company with a security team will balk at that finding in a report; it would make your team look like they didn't know what they were doing. No, this isn't a norm.


Although I agree with the ideal behavior you mentioned, your comment about "every company with a security team" is not true. Google does this with their firebase product using a combination of long and short-lived tokens.

I simply offered a reasonable solution to revoke JWT tokens. Would I use them in a project? Probably not. Even so, if an attacker has compromised someone's session, it's probably too late.




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

Search: