Hacker News new | past | comments | ask | show | jobs | submit login
Stop using JWT for sessions (2016) (cryto.net)
248 points by enraged_camel on Nov 1, 2018 | hide | past | web | favorite | 245 comments



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.


If an attacker can read the user's localStorage you have much bigger issues to worry about: XSS, physical access, no TLS, etc. With any of those, the token is the least of your concerns; an attacker can make API calls, read the data, etc anyway, no matter if its JWT or sessions.

So, IF (which is a big if) you implement everything correctly then you have to do two "extra" steps with each tech:

- Blacklists (or Whitelists). You _do_ need these, because you want users to be able to logout. Then it's a lot more like sessions... oops.

- CSRF protection for sessions. These become trickier to manage in SPA and just become sort of a "token" that you need to refresh on each call... oops.

It seems that the shortcomings of each technology makes it to lean towards the other. The good news is that both of them are theoretically secure, the bad news is that to reach that level you have to do a lot of work (on both!) with a lot of room for error.

That is why I personally prefer JWT with SPA and CSRF with traditional servers, since the amount of new code that I have to write is a lot lower so chances for error are quite lower. For instance, see the (very brief) documentation of the CSRF for my project server.js: https://serverjs.io/documentation/router/#csrf-token . If I had more time I'd write something similar for JWT.


"If an attacker can read the user's localStorage"

9/10 the attacker is the user.


Since the topic is authentication, that is an independent concern, right?

Edit: unless you mean something about making the user do something. If it's running a script, it's the same in both ways. Now, if it is about the user retrieving and sending the token, the attacker could still ask the user to manually get the session cookie or the localStorage JWT. The cookies might be protected not to be accessible from JS, but they are still in the browser.


It's an interesting reading (together with second part), but should be taken with a grain of salt.

Stateless sessions are useful under load and not necessarily under reddit-size load. I like 2-layered setup actually with long-expiration (1 year) "session" cookie kept on `auth.example.com` domain and short-lived (1 hour) JWT-cookie kept on `example.com` domain. JWT can optionally include permission for dangerous operations which is not automatically granted.

You still need redis to take care of forceful expiration, but that would be very simple call to the small list (1 hour of force-expirations if you reissue jwt-tokens on hourly basis), not data-fetching calls. We can keep this in a tiny redis-cluster colocated with frontend machines. JWT would have enough data to personalize cached templates. This will render really fast.

API-backend, database and larger redis-storages can be kept deeper in logical server-room and be accessed only on cold requests


I generally agree, but to me "short-lived" would be below a minute. The point of the short-lived token is to make many clustered requests (as when a page loads a lot of assets) cheap because you only need to check a token. Any other human interaction, which takes longer, should go through the server-persisted session logic.


hi ;)

I guess that depends on exact use-cases. Below-a-minute is fine for tightly controlled environments, but publicly-used web-apps usually have a sweet spot with TTLs between 10 and 60 minutes. More requests = shorter TTL, less requests = longer ttl (as it would be counter-productive to have more auth-requests than data-requests)


Indeed hi! :)


The idea of JWT is basically you give your user a "token" that can be used against many "services" while servers don't have to persist the state of that token, just be able to read the encrypted token, and trust the information in the token. The obvious issue is the server cannot easily revoke that token without blacklisting it, therefore persisting that token somewhere on a blacklist on the server. If you are going to make a lookup for a token in a blacklist you might as well look up for a session ID to being with.


Traversing a list of 100,000 users/sessions in a db to pull up the session is a different beast compared to traversing an in memory list of 10-100 revoked JTIs in redis. It is a lot less data to store and optimize (the full session vs. a small list of revoked JTIs)


> Traversing a list of 100,000 users/sessions in a db to pull up the session is a different beast compared to traversing an in memory list of 10-100 revoked JTIs in redis. It is a lot less data to store and optimize (the full session vs. a small list of revoked JTIs)

I don't see why one can't use redis to persist sessions at first place and why your list of revoked token would be limited to 100.


It does depend on how many users you have and your TTL for the JWT, but if you have a 10 minute time to live, you only need to store the revocations for 10 minutes in redis since otherwise the tokens themselves expire. (Just an example, sub 10 minutes with 24 hours or whatever)


It's not explicitly limited to 100, but only a small portion of tokens will be revoked, so your revocation list will be small.


I'm not 100% sold. I think JWT's are fine in some situations as long as you know the limitations.

Regarding session invalidation, I would handle this in two ways.

1. Support a "Log everyone out" function by storing a simple version number in the JWT. If the version constant in your app is different to the number in the JWT, it is invalid.

2. Support a "I need to logout user X function" by storing a blacklist of tokens in your RDBMS. If your RDBMS goes down, you have much bigger problems. A blacklist will be very short, and often completely empty, so lookups will be significantly faster than checking a session table. You could also use Redis or an application cache for storing the blacklist, with an RDBMS fallback.

Regarding storage, there's nothing stopping you putting your JWT into a cookie with the HTTPonly flag set. So long as you're not storing too much in token, they will be less than 4k and so will fit.


> 1. Support a "Log everyone out" function by storing a simple version number in the JWT. If the version constant in your app is different to the number in the JWT, it is invalid.

If you've got the iat property, you could just use that. i.e. anything issued before x time is invalid.


>>2. Support a "I need to logout user X function" by storing a blacklist of tokens in your RDBMS.

Again though, the whole point of JWT is to completely avoid server-side state management. The moment you store state somewhere (whether a list of valid tokens or a shorter list of invalid tokens) you have re-invented the concept of sessions, so why not use that instead?


How about instead of "the whole point of JWT is to completely avoid server-side state management" we think of it as "the whole point of JWT is to reduce the overheads of server-side state management".

In my answer above, I was pretty clear about how the load on the database can be significantly reduced with a blacklist and optionally Redis out front, thus allowing JWT to significantly reduce the burden on the database.


Why would the lookups be faster in a small/empty table? If the lookup is by indexed ID it should be trivially fast either way.

Furthermore, you can store the session information in Redis by ID, too, to reduce DB burden.


Well, the RDBMS solution is precisely a stateful infrastructure that (the author claims) “defeats the entire point of using stateless JWT tokens.”


This JSON Web Token Security Cheat Sheet[1] or this python script[2] are both quite useful when reviewing anything using of JWTs.

[1] https://assets.pentesterlab.com/jwt_security_cheatsheet/jwt_...

[2] https://github.com/ticarpi/jwt_tool


Those are great! I find the OWASP cheat sheet[1] useful as well but I’m going to keep the pentesterlab cheetsheet bookmarked for easier to digest to similar data. Thanks for sharing!

[1] https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_S...


I used to be very opposed to JWTs but I can see some interesting use cases for them now, at least when using the variant based on public key cryptography. For example, if you need/want the ability to verify token validity locally (i.e. without making a call to you auth backend) they are very handy: Generate a token, sign it using the auth backends' private key and clients can verify it using the public key.

That said, I wouldn't use them as a default authentication mechanism as invalidation requires a connection between clients and auth backend again, as many people here pointed already out. It might still be easier to establish such a channel though than continuously verifying traditional tokens via the backend, which you can solve differently though as well: For example, for our APIs at KIProtect we cache validity information for (hashed) access tokens for 60 seconds on the API server, and we refresh the tokens in the background 30 seconds before they expire (if triggered by a request). Like that we can ensure that invalid tokens cannot be used after a short grace period (60 seconds is good enough for us) while not slowing down clients that perform many API requests as the token needs to be fetched only for the first request and will then never leave the cache (as it gets updated) if the client performs at least one request roughly every 60 seconds (or more often).


Sorry, I'm going to be a contrarian here. JWTs can be good.

First, cookies-vs-local storage is a non-issue here. If a JWT is small, it can be put in either location.

The only real issue is, should session state be on the client or on the server?

Suppose you have an app with large numbers of users, doing hundreds+ of queries per second across many boxes. If you put session state on the server, then you must either 1) have sticky sessions, which generate problems when a server goes down; 2) have a central session server, which generates scalability and reliability problems; 3) have a Redis-like distributed system, which either a) must do one or more server-to-server round trips on every client call to validate the session, or b) cache session information on a node.

When latency matters, an extra server-to-server round trip is a non-starter. And if you do server-side caching, then cache invalidation and JWT invalidation present similar problems. ("There are only two hard things in Computer Science: cache invalidation and naming things.").

Server-side sessions don't really buy you much. If you're using a system that has them built in, lovely, but if you're not, it's a lot of extra work.

JWT is a good solution to the problem. You can store small amounts of session state on the client and simplify the whole system. You can avoid having to do lookups to determine state variables. (What database was the user connected to? Is he in mode A or mode B? What timezone? What language?) Put a short expiration on the token (a minute? five minutes?) and force the user to re-validate against some data store after that.

The benefit is that you greatly reduce internal server traffic at the cost of not being able to invalidate a session within your short timeout interval. Depending on your app, letting someone remain an admin for an extra minute is not a big deal.

And if it is a big deal, you can still handle it by sending a message to each node telling it to invalidate that particular user. A pain, yes, but the tradeoffs may be worth it.


> If you're using a system that has them built in, lovely, but if you're not, it's a lot of extra work.

They're built in with PHP and most Node.js frameworks I've ever worked with.

I'm not sure about Python, RoR, etc. web frameworks but the engineering overhead for adopting server-side sessions should approximate 0, since they're usually the default.


Ruby dev here, Rails is pretty good at sessions. I hear the Python crowd have some great stuff too.


Also you're typically already looking up the user so you can just add a token_version property on the user and increment it when you want to expire tokens for that user. JWT problem solved without any additional work for the db.


Like many other articles on JWTs, this one mischaracterizes the trade-offs that exist.

You can invalidate individual JWTs, you simply store the blacklisted token IDs in a table in your database. Implemented naively, this results in a request flow in which the database is still accessed on every request, defeating one primary purpose for using JWTs. However, there are better ways.

Since the blacklist is just a collection of random token IDs, it isn't sensitive. You can store it anywhere, including in memory in your web servers or in a distributed cache. You can wait during blacklist/token invalidation events until the newly blacklisted token has been propagated.

Using the above, JWTs enable a design space trade-offs that doesn't exist with session tokens. You trade slower blacklist requests (logout, one time confirmations, etc) and minor auth complexity for one fewer db access per request, more flexibility, and more uniformity if you use oath or similar.

All of this is around 90 lines of python in my Django site.


> Since the blacklist is just a collection of random token IDs, it isn't sensitive. You can store it anywhere, including in memory in your web servers or in a distributed cache. You can wait during blacklist/token invalidation events until the newly blacklisted token has been propagated.

This is very dangerous advice. If you're advocating for the addition of a fast blacklist storage, you really haven't changed your previous pattern complaint, but the dangeous bit of your advice is implying that it's simple to store "anywhere" such as "in memory in your web servers."

That is a bad idea. If your authentication model is depending on an explicit stop-list to invalidate tokens, then you need to take the integrity of that data set very seriously or be at serious risk of credential replay attacks. A loss of integrity in this data store in unacceptable. Even some eventual consistency can be dangerous (but is generally accepted as a risk if the time envelope is acceptable).

It's much better (from a security standpoint) to have very short lived tokens and constantly revalidate. The revalidation cycles CAN be full database cycles since they occur at a reduced rate, and you can then have more sophisticated policies there without too much trouble.

> All of this is around 90 lines of python in my Django site.

I don't doubt it, but trying to scale your advice to a product or to an environment with actual concurrency opens up a lot of complications you're not considering.

JWTs are, by and large, just an inferior spec with too many brittle edges. You need to be really careful to properly implement JWTs in your environment. You're much better off either sticking to rich session tokens or using something like Macaroons.

P.S., not only is the phrase "black" list steeped in a history you may not realize you're invoking, but it's much less precise than the term "stop list". Please do consider this.


> then you need to take the integrity of that data set very seriously or be at serious risk of credential replay attacks.

This is no more true in my technique than in session tokens. You have to invalidate session tokens carefully, most simply using serializable database transactions. You can do the same with the blacklist.

People also put session tokens in memory, and your concern about caching and consistency applies equally in that context. The blacklist is in some ways easier to manage because it is not sensitive like a session token. It's more difficult in other ways, but IMO harder to get wrong.


> This is no more true in my technique than in session tokens.

Which is to say that it's equally true, but you're advocating for a more ephemeral storage.

> People also put session tokens in memory, and your concern about caching and consistency applies equally in that context. The blacklist is in some ways easier to manage because it is not sensitive like a session token. It's more difficult in other ways, but IMO harder to get wrong.

If you're leaking session tokens your entire authN/authZ system collapses I guess that's not wrong, but it's not clear that your invalidation session is as insensitive as you suggest. For example, if your token generator's RNG has flaws, you're essentially enabling that discovery via a public stoplist store.

Ultimately, it's weird to imagine using something like JWT and having a request-to-authZ scheme. You have all the equipment to do short-lived renewable tokens, and then you get invalidation (to the resolution of your timing) for free and better scaling properties and a whole host of other security benefits.

I personally strongly discourage people to use JWTs at all, but if you are at least take advantage of them!


> not only is the phrase "black" list steeped in a history you may not realize you're invoking

nonsense.

> but it's much less precise than the term "stop list"

tenuous. the term blacklist has existed since at least the 1600s[1]. i would be surprised if its meaning was not well understood.

[1] - https://en.wikipedia.org/wiki/Blacklisting


Ultimately the reason JWT works for people regardless of these flaws is that almost no one ever uses it in an environment where it scales past the point that something like a blacklist lookup is a crippling flaw in the infrastructure.


I wish this were true, but I've seen JWT deployed at some large shops with terrible technical and security outcomes and everyone seems baffled how a spec could be bad. It's unfortunate.

P.S., while unrelated to my technical argument, please see my postscript above.


Many in the comments are curious about blacklisting JWTs. No, you don't need to put the whole token on a blacklist. Give your tokens reasonably unique JTI claim[1] and put the affected JTIs on the blacklist.

The benefit of JWTs is that token blacklists are usually much shorter than hypothetical token whitelists.

[1] https://tools.ietf.org/html/rfc7519#section-4.1.7


What if the blacklist becomes unavailable? The author addresses this in part 2.


Then your application is unavailable, just like it would be if your session table went away? I don’t get this complaint.

The advantage is that you can use a loosely consistent datastore with expiring entries and few records at any given time. There’s lots to love about the approach.


> Then your application is unavailable, just like it would be if your session table went away?

The author replies: "congratulations, you've just reinvented sessions, with all their problems (like centralized state) and gained nothing in the process", and also with an implementation that is "less battle-tested".

My own opinion: loose consistency can work in some circumstances, but seems a security risk in others.


But we've drastically reduced the amount of central state, the complexity of managing it, the time it takes to access it, the danger of losing it, the performance impact of updates to it, while making the happy path to your site faster.

Revolutionary, maybe not, a solid improvement on the traditional design of sessions, I think so.


> Unless you work on a Reddit-scale application, there's no reason to be using JWT tokens as a session mechanism.

And what should I use when I do work on a Reddit-scale application?


https://paseto.io/

>>Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.


Macaroons. JWTs are a total nightmare from a security perspective in an organization trying to provide a microservice environment. There are way too many potential mistakes permissible in JWTs spec. They nearly always come up when you're stretching across a pair of language environments and a few library versions.



From my time browsing HN, the consensus seems to be "don't use JWTs". What is the preferred method of client authentication for simple APIs these days?


Disclaimer: not a front-end dev.

I'm not sure what the issue is with a simple session token, to be honest. OWASP have guidelines on this that cover the bases pretty well, I think.

Store hashes, not tokens directly.

Expire them server side.

Don't accept user input (within reason). You generate and offer the tokens, you know what they look like, you can bounds check / sanity check appropriately.

Perform some additional checks as necessary (e.g. invalidate a session if remote IP changes).

If you have more security critical parts of the infrastructure, require re-authentication and more short-lived sessions for those. The obvious example would be how Amazon pretend you're "logged in" until you go to click My Orders, then you get auth gated.

I tend to think that people over-complicate this stuff a lot as a premature optimization for scaling.

A simple python script backed with some DB will do hundreds of hits a second to an auth service without really trying. If you need more than that, optimize. If you need more than 10k hits a second on a regular basis then you're probably at the point of hiring someone who knows this stuff.

Just my 2c.


HTTP basic authentication (of course over HTTPS)? Very simple to use and all the tools, such as curl, support it.

It is used even by fairly large scale services like Twilio[1].

[1] https://www.twilio.com/docs/usage/api


OAuth access tokens? The difference being that JWTs contain information directly, as well as an access token to hit any APIs, while an access token contains no information on its own - thus solving the revocation issue.

Now I don't think anyone really uses JWTs for API authentication, or they should not at least. APIs should still be gated by access tokens.


Tokens checked against server-side sessions. OAuth 2 is common to obtain them. (The tokens can of course be JWTs if you want their content to be transparent, but the key is to not rely on the metadata in the token alone for verification)


Session cookie, I believe.


I use JWT for authentication on my current project. And I eventually ended up having to check the user's 'role' in the database for authorization before I let them do anything anyway (and consequently, revoking someone's user role or suspending their account has immediate effect).

I saw many examples on the net of people putting role/authorization and various other types of session information within the JWT and it just looked insane to me.

Ultimately, I think what I ended up with is only a minor win over just generating random tokens and maintaining a "sessions" table. Probably not worth the effort and likely won't use JWT again.


> I saw many examples on the net of people putting role/authorization and various other types of session information within the JWT and it just looked insane to me.

Why is this crazy? If you encrypt your JWT with a private key that is only known to the server, then any modification to the JWT will lead to an invalid token.

The only thing here is that the list of roles granted is visible to the user if they were to decode the JWT, which is something you might not be entirely comfortable with but I don't think it's totally crazy.


I suppose it depends on the application. I wanted to be able to revoke a user's permissions and not have to wait until token expiration for them to be locked out.


Can anybody recommend a tutorial or book on how to do Authentication properly? Say for the usecase SPA + REST API.

If I google quickly I mostly just find tutorials that turn out to be ads for some 3rd party service. Or other beginner tutorials that don't seem very trustworthy.

I feel like I'm missing something, since this should be a very common use case for most (web) apps.

(Bonus if it also covers User Authorization, i.e. different roles that are allowed to do things or not.)

Thanks.


We use JWT for validating simple handoffs from server to server, allowing someone to briefly access a resources such as downloading a file from a server designed to do that. We set the expiration to only an hour, and JWT is excellent for this use case as it means we don't need to implement our entire login and database workflow on a machine that just serves files.


"You cannot invalidate individual JWT tokens"

This is not exactly true. There are lots of things you can do to expire tokens. For instance, every JWT has a creation date stamp so you could say on the server side all tokens created before Time.Now() no longer accept as valid. Allowing users to expire ALL tokens, etc.


> you could say on the server side all tokens created before Time.Now() no longer accept as valid

But that's not anywhere near "invalidate individual JWT tokens" - you're invalidating -every- token before `Time.Now()`. Unless you store a different "valid after" time per user and then you're back to storing sessions in the DB, aren't you?


So you need to maintain state whenever tokens get invalidated, and check this state on every token-bearing request. At that point why not just fetch the rest of your session data out of your database?

And if it’s some kind of global cutoff - well, now every revoked session comes with a log off for every user, which is a usability nightmare.


Can someone explain how this is similar/different from the default ways Ruby on Rails stores sessions?

I know they're encrypted and signed, and all the session data is stored in the cookie. This seems to work great, what is the downside of just doing this method (for say, bigger sites)?


The only downside I've ever encountered to Rails default sessions involves authenticating against other backend endpoints from JS applications served by Rails. Which really has very little to do with Rails itself...


So do you know the difference between that way and the JWT way? All these different methods are kind of confusing, and it seems Rails does it a great way, and everyone hates JWT... but arent they basically the same thing?


The arguments here says you can't invalidate a JWT. Exactly!! JWT IS A TOKEN. You can use it for refresh (invalidation & renew) and access (authorization) tokens. There's so much disinformation about JWT. The end result is some not invented here "JWT".


Yes but you're missing an entire chunk of context. The problem mentioned in this article is saying it's bad that you can't invalidate a JWT WHEN using them for authentication and/or authorization. This makes it impossible to do things like disable/kill a session that has been deemed hostile or dangerous (like a stolen account).

Now to build on this, good defense-in-depth can help here. For example if you use JWT only for authn but kept authz 100% server-side. You could then revoke the users access to everything so that the blast radius is confined. The bad actor could still authenticate into the system but would not have access to much or any resources.


A huge part of the issue with JWT is that it's often used wrongly.

I'm currently in a project where JWT is used. We have a list of searchable items and each one of them holds a gallery of photos - up to 5MB per photo.

The issue at hand is that these photos are served from a certain endpoint which we eventually want to secure, but the back-end's idea here is for the front-end to download the photos via XHR putting the token in the Authorization header - they refuse to send the JWT via cookie saying that it's "incompatible with JWT".


Burp Plugin for JWT decoding / attacks https://github.com/mvetsch/JWT4B


JWT is an authentication token - it isn't session state. Session state changes during the lifetime of a session. Whereas the authentication token does not - other than for renewals. Overloading the authentication token to also store unrelated domain concepts such as shopping basket items is a gross abuse of its purpose. Given that generating a JWT token is computationally expensive I really doubt that many people are doing this like the premise of the article suggests.


I think by payload they mean the JWT can "store" actual permissions. Not state such as a shopping cart, but a payload composed of permissions granted to this client to do this or that in this app.


Storing security claims i.e. permissions in the JWT token is a valid use. This isn't session state. Those permissions don't change, they are in fact cryptographically immutable, until the next renewal of the token. Whereas by definition "state" does change.


I know it's a valid use (I use JWTs!). But it is state, which is why you have a payload and not merely an id. State doesn't have to change; that's mutable state. (Permissions do change, by the way, only less frequently than a shopping cart!).


We have a requirement that users be logged out after 30 minutes of inactivity, so JWTs are perfect for our use case.


Users may wish to log out sooner than 30 minutes too.


Except cookies are quite literally perfect for that - set the expiration to 30 minutes from issuance and it will automatically be expired with inactivity. Or bumped on activity.


You shouldn't be trusting the client to expire your sessions for you. (That is, relying on a cookie's expiration to actually expire the session.)

(Now, if you mean looking up a session token in a database and checking that for expiry, then yes, that works, but the only real difference between JWT and tokens then is the latter requiring a database query.)


The more important difference between JWT and tokens is that the former requires you to trust that your encryption mechanism is foolproof, whereas the latter doesn't.


Session tokens (an unguessable identifier that identifies server-side stored session data), while they don't need to be encrypted or signed, the identifier should usually be cryptographically random, however, so you can still get into trouble there.

JWTs don't have to be encrypted. (And IME, typically aren't.) They need to be signed, but the algorithms that do this are common, well-understood algorithms. Some of them are likely being used by TLS to protect your session anyways. If they're not foolproof, you have bigger issues than JWT.

If you are referring to the implementation, then sure, but the same concerns exist about your implementation of TLS, and the same advice applies: use a well-known, hopefully well tested library.


Cookies are implementation dependent. It's quite possible that a user agent doesn't honour what you instructed it to do regarding the expiration of a cookie.

JWT however... your server side can validate its own token in whatever ways it wants (including expiration). You can't get more secure than that.


If you checking the session on the server, you might as well just use a token in a cookie. The point of the JWT complexity is to avoid that.


JWT tokens being passed via cookies is a valid use case.


What about a stolen user password and an actor, who already uses your token? Can the targeted user kick him out by resetting his / her password?


Sure why not? Each user account has a counter or timestamp on it in the user registry. If the JWT token in the request passes crypto validation but it has an older counter/timestamp than held in the user registry then it is deemed invalid and the user must re-authenticate. This is still a horizontally scalable design.


Then you are back to statefulness if you need to check this magic "count" every request.


You can horizontally scale counters with CRDTs, specifically the G-Counter. Whereas it is harder to scale a (usually) opaque blob of application state a.k.a. session state.


This “user registry” of yours sounds an awful lot like a traditional session table in a central DB.

Could you elaborate a bit on that in terms of horizontal scalability please?


User registry can scaled however you want it to be. It is simply the service that manages user accounts, passwords, permissions, and of course issues the JWT tokens. Equally it can be a horizontally scaled micro service.


No, but that's 3 systems away upstream anyway - thus the 30 minute timeout.


Short lived JWT is ok for stitching desperate micro services together as it saves round trips to verify things but I prefer mystery tokens when using more intimate services that have access to similar underlying stores to verify them. Basically use them appropriately and they are fine.


How I use JWT:

an encrypted storage of the user's authentication credentials, stuffed into a persistent domain cookie.

Thus, JWT is just a standardized way of encrypting a json payload and storing/retrieving it in a cookie.

works for me (my arch is stateless), and seems orthogonal to the arguments described in the article.


Can someone explain the Cookie vs LocalStorage thing? You can access cookies from Javascript, so how is localstorage worse? Assuming an attacker can execute arbitrary js in the browser (the model provided by the article).

edit: Thanks for the answers - httponly, makes sense.


If you set cookie httpOnly, Javascript cannot see it. So some malicious third party library or XSS attacker cannot steal session. It can still do requests and cookies would be included, so stealing is the main difference.


https://portswigger.net/blog/web-storage-the-lesser-evil-for...

(It not only explains what others have said about httpOnly, but it also goes more in depth on the tradeoffs between the two.)


The author glosses over this but what they mean is that you can set the HttpOnly flag on cookies to prevent them from being accessed via JavaScript.


But is it still sent automatically on ajax requests by browsers?


Yes.


> You can access cookies from Javascript, so how is localstorage worse?

You can access it via JS only if you don't set the httpOnly flag on the cookie.


You can't access a cookie from JavaScript if it is marked with the HttpOnly flag.


Relevant: https://paseto.io Platform-Agnostic Security Tokens

>Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.


An update that has not been added to the article yet due to a lack of time: even if you do have a valid usecase for stateless tokens, you probably want PASETO[1] instead of JWT, due to the inherent cryptographic design flaws within JWT[2].

I'm probably not going to engage in the discussions on here this time, as it was an incredibly mentally draining experience last time; the vast majority of arguments were either already addressed in the article itself (which the commenters clearly had not read and understood in full), or argued against a misrepresented claim. It got so bad that I had to dedicate a whole second article to it[3].

If you have a genuine question or concern about the article rather than an Angry Internet Comment, feel free to e-mail me; my contact details are at the bottom of the article. No guarantees on response time as I'm juggling a lot of other plates at the moment, but I'll eventually get around to responding :)

EDIT: Another addition... this presentation[4] gives some insight into the history of JWT-for-sessions and why it became so popular. A spoiler: it wasn't for rationally-technical reasons.

[1] https://paseto.io/

[2] https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...

[3] http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...

[4] https://youtu.be/GrLtOjCTB1s?t=1h2m44s


Are there any engineering books that have a collection of best practices for internet engineering problems similar to this one? I find that most of this information is learned colloquially, or through what other frameworks do.


I used JWT for a SPA site using Vuejs with Django backend. The project became a nightmare and I had to abandon it because of the increasingly complex security issues. The client was not happy


what about setting a secure cookie with a jwt, and expiring it when you log-out? what is the utility in server-sessions? (aside from the ability to revoke it at any time from the server.)


> and expiring it when you log-out?

and how would you do that if the user doesn't actively log out of a service?


set the cookie in the response to their next request to you?

specifics depend upon the language and framework used on the server, etc...


Do you mean "delete the cookie" with "expiring it"? Then that doesn't help against an attacker which already has obtained the JWT, they can still use the JWT until it's expiry.


what i propose: 1) login = sending username and hashed password (over tls) that does a bcrypt etc, compares to bcrypted etc hash in db, if correct = provide a jwt set as a cookie with "secure httpOnly", jwt should be logged.

2) logout = request to server, server sets cookie with "token=empty;expires:1970yaddayadda" as it's httpOnly there's no client-side messing baout with the cookie otherwise

3)_all pages send cookie with each request, so must be tls

upon logout, cookie has empty token, must be re-set with new token. old token, if stolen, can be compared with logged history, etc.

what do you see, in terms of flaws?


so, what about a single-use token you exchange for another token (or not)?


None of what the author is writing about concerning the inability to revoke stateless tokens is particularly novel. We have the same problem with SSL/TLS PKI. Once a cert is revoked, there is no reliable way to ensure that its revocation is enforced among all clients. The world has proved to be content with accepting this risk.

Protip: be sure to read what I said before you use the b-word in your responses.

Additionally, the sarcastic flowchart that is included in part 2 of his post makes some pretty open-ended assumptions. "Just take down the blacklist server" is my favorite.

edit: clarification.


This seems to be targeted at web apps. I take it I don't need to remove JWT token support from all of my mobile applications?


The problems with JWTs go well beyond "how do we do session invalidation." However, since mobile apps tend to be SSL encrypted on devices that don't reveal contents to users, you've often got a lot more leeway than the transparent condition web apps suffer.

I know my app didn't have any real security challenges until we had a website with actionable data.


I wrote a scala JWT library on a weekend when I was more of an intermediate developer (6 years ago?) and it started to be used. But I never ran it in production and flaws were found in it. As a consultant 5 years later I found a customer using it and told them to use someone else's :).

I'm not a security expert - one of the strengths is one of the weaknesses - that's it's a simple spec to implement so your average Joe can implement it, but imperfectly.


Wow, I wish people would stop complaining about JWTs..

JWTs are a silver bullet, but it's nicer than rolling your own signing scheme.

Don't run with scissors, don't do security if you don't understand your primitives..


The alternative to JWTs isn't rolling your own signing scheme. Use PASETO instead. https://paseto.io

But the issue being discussed in this article is about how JWTs are being used, not JWTs themselves.

> don't do security if you don't understand your primitives..

The security industry needs to do more to provide tools that do security for people who don't understand their primitives.


> JWTs are a silver bullet, but it's nicer than rolling your own signing scheme.

you can use HMAC to sign session cookies as well, the issue isn't about signing.


Obviously written by a non-expert. Half of the claims here are false and he only references other blogs. OWASP is nowhere to be found.


This comment is completely unhelpful without detailing which half.


Obviously written by a non-expert /s


I’m hoping this is sarcasm.


> OWASP is nowhere to be found.

Is this satire?


Incorrect, these are not comparable things.

JWTs are a standardized way to securely encode data into a self-contained token. Cookies are special headers added to HTTP requests to store state.

You can absolutely use JWTs as the payload of your cookie. How you pass the session data back and forth is separate from how you encode the session data. Most web frameworks have their own token formats, but you can easily change them to be JWTs.

Using the Authorization header just means you're using a different header, and since it's not automatically sent then you don't have the CSRF issues with browsers, but now you have to manage it more manually in your client-side code.

Regardless of how you encode your tokens, if you want to do instant expiration or other live data lookup then of course you'll need to manage that on your server, whether it's a database, in-memory cache, or something else. The split between where state is managed is irrelevant to the physical transfer and encoding.


> This is just FUD.

No, it's an engineering argument that makes several testable claims about JWTs, sane session protocols, and how the two are mutually exclusive.

> JWTs are a standardized way to securely encode data into a self-contained token. Cookies are special headers added to HTTP requests to store state.

The difference is this:

  - Sane sessions
    - Random unique identifiers stored in a cookie
    - Upon receiving the cookie, the web app looks in the
      filesystem/database/etc. for the data associated with
      that session
    - Can be invalidated by deleting the server-side storage
  - JWT-based sessions without server-side persistence
    - All session state is encoded into the cookie
    - All the trust is outsourced to the client to invalidate
      sessions.
The other arguments follow from this difference.

A strongly worded argument against a bad engineering argument is not FUD.


As stated, how you encode state is different from how you transfer state. It's not an engineering argument if you don't understand the fundamentals.

JWTs are a encoding format. You can just put a single random unique identifier in there if you want. And you can use cookies to transfer JWTs automatically instead of using the Authorization header. Either can be checked on every request by the server for further validation.

The limitations you wrote are completely arbitrary.


It sounds to me like you don't understand what Sven is arguing against in the article.

> JWTs are a encoding format. You can just put a single random unique identifier in there if you want. And you can use "cookies" to transfer JWTs automatically instead of using the Authorization header. Either can be checked on every request by the server.

This is totally irrelevant!

Does your cookie (JWT, PASETO, or whatever) contain more than just a unique identifier?

The argument is: Are you storing things like user_id=13455&is_admin=false in the cookie instead of having that entirely server-side? Don't do that, it's a bad design, store that server-side instead.

That's literally the argument.


How you ENCODE data is different from how you TRANSFER data and neither are related to storing state completely on the client vs the server.

You can pass JWTs as cookie data, and you can store nothing but a single session ID in the JWT itself. They are completely orthogonal and have nothing to do with the actual argument. It's misleading when the title and the entire article talks about JWTs vs session/cookies.


> That has nothing to do with JWTs.

Progress! You're so close to understanding the article.

It wasn't ever about JWTs in particular. It was a response to a widespread engineering antipattern of "storing all session state in a cookie then maybe encrypting or HMACing it so you don't have to store anything server-side". JWT was just how developers tended to implement this antipattern.

Like, literally, that was the entire point of the entire article that Sven wrote.

The examples of apps that do this have become less common since the article was written, so if you're confused by that, you're just missing context, and that's OK.

As for "anti-JWT" arguments, I've made them separately from the argument of Sven's claims. JWT is an error-prone cryptographic design. I wrote a replacement standard called PASETO that doesn't contain these foot-bullets. I still don't recommend people use PASETOs for sessions!


Yes I understand that it's about client vs server managed state and the balance between. You can see this noted in my very first comment.

But this article is titled and talks about JWTs at great length with pros and cons, which is completely misleading. The fact that many of the comments here are only talking about JWTs vs cookies and skip over what and where the state is managed only further reflects that confusion.

Perhaps a better article should be written and posted.


> Cookies are special headers added to HTTP requests to store state.

The topic is more about session VS JWT, not cookies vs whatever local storage is used in place of cookies.


"Session" does not mean anything. It is state, usually stored and sent in cookies, but just as easily stored and sent as JWTs/Auth.


Yes it does mean something. a session is something that is identified by an ID on the client and is persisted on the server with the corresponding ID. It's a concept. It doesn't have to be stored in a cookie. A session can be represented by a token and stored in any client side storage.


Ok, yes, session is a concept. Session state can be stored in its entirety or split between an ID and body.

You can send it all to the client or you can send just an ID and lookup the body on the server.

You can send it to the client via the cookie header or via the Authorization header or something else.

You can encode the data (sent via cookies or auth header) as a JWT or your own encryption scheme.

These are all different technologies working at different layers, which is why comparing JWTs vs cookies vs sessions doesn't really make sense.


> Ok, yes, session is a concept. Session state can be stored in its entirety or split between an ID and body.

the storage of session has absolutely nothing to do with the session. I'm not sure why you keep talking about Cookies it has nothing to do with the problem. JWT can be persisted with cookies as well.


Yes, my original and last comments already said exactly that... I'm not sure what you're arguing at this point.




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

Search: