Stop using JWT for sessions (2016)
cryto.net
cryto.net
> 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.
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!)
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).
So just use a session library with expiration implemented, don’t take one without and add it on yourself.
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.
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...
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.
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?
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)
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.
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.
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.
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.
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.Instead of `SELECT isValid from tokens where token="<TOKEN>"`, why not `SELECT user from sessions where session="<COOKIE-SESSION>"`?
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?
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.
So if a single user's token gets compromised, you force ALL users to log in again?
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.
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.
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.
Please do go find your tribe of condescending devs with fragile egos.
How can you invalidate my server now?
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.
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 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.
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.
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.
JWTs can be stored in cookies. So I don't understand why you say this.
>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.
It's trivial to store a token version on your user object and invalidate any token that has the wrong version.
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.
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?
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.
Could you link to one of your long explanations ? I'm interested in a rebutal but understand it's annoying to repeat yourself.
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.
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.
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.
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 you invalidate either is the same, whether its a database lookup or something else. Article and most of the comments are wholly inaccurate.
Whether you manage state 100% on client or share it between client and server is an entirely separate topic.
Otherwise, they could just use a long random string and maybe HMAC it and call it a day.
There are some common usage conventions but the limits described in this article are completely arbitrary.
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...
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.
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?
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.
...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.
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...
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'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.
> 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.
But, what are you getting for this added complexity--above and beyond what "traditional" user session management provides?
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.
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.
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.
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.
what fields are in this session object besides an identifier?
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....
So, should I think that you agree with the article?
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.
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.
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.
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?
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.
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.
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.
9/10 the attacker is the user.
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.
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 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)
[1] https://assets.pentesterlab.com/jwt_security_cheatsheet/jwt_...
[1] https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_S...
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.
If you've got the iat property, you could just use that. i.e. anything issued before x time is invalid.
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?
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.
Furthermore, you can store the session information in Redis by ID, too, to reduce DB burden.
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).
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.
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.
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.
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.
P.S., while unrelated to my technical argument, please see my postscript above.
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.
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.
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!
The benefit of JWTs is that token blacklists are usually much shorter than hypothetical token whitelists.
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.
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.
Revolutionary, maybe not, a solid improvement on the traditional design of sessions, I think so.
And what should I use when I do work on a Reddit-scale application?
>>Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.
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.
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.
It is used even by fairly large scale services like Twilio[1].
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.
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.
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.
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.
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.
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?
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)?
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.
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".
Could you elaborate a bit on that in terms of horizontal scalability please?
(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.)
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.
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.
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.
edit: Thanks for the answers - httponly, makes sense.
You can access it via JS only if you don't set the httpOnly flag on the cookie.
(It not only explains what others have said about httpOnly, but it also goes more in depth on the tradeoffs between the two.)
>Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.
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.
[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...
and how would you do that if the user doesn't actively log out of a service?
specifics depend upon the language and framework used on the server, etc...
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?
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.
I know my app didn't have any real security challenges until we had a website with actionable data.
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.
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..
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.
you can use HMAC to sign session cookies as well, the issue isn't about signing.
Is this satire?
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.
The topic is more about session VS JWT, not cookies vs whatever local storage is used in place of cookies.
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.
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.
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.
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.
> 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.
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.
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!
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.