4 ms·
You could have short-lived JWT tokens so it wouldn’t matter because they only last 5-10 minutes or less. They could be refreshed or if using OAuth, simply redir
by lstamour 5y ago
You could have short-lived JWT tokens so it wouldn’t matter because they only last 5-10 minutes or less. They could be refreshed or if using OAuth, simply redirect an unauthenticated user and they’ll be reissued based on the cookies stored by the OAuth provider… assuming the redirect doesn’t break your app of course.
Alternatives? First, you could live with the possibility that tokens are valid for some period of time after logout because it usually doesn’t matter - generally you delete the cookie and the user is logged out, even if technically they could restore the cookie later. They won’t, unless you’re under attack.
Other alternatives: You could use Redis for session data as pointed out with the random key in a cookie and delete the session when done to invalidate the cookie.
You could also use Redis to keep token IDs that you want to invalidate or block. You could use something like Open Policy Agent to distribute a list of invalidated tokens to each server.
Finally, you could send your JWTs to a centralized authentication service — single point of failure, yes, but you could record invalidated tokens to memory and responses are very quick and easy to audit. With careful planning you could reduce the risks in having a single central service to validate issued tokens.
I’m sure there are other ways to mitigate this risk. The general reason why folks don’t recommend JWTs is because it’s too easy to make mistakes in the validation logic. But the same is true (with different possible mistakes) when you roll your own session cookies. At a certain point I think you have to either assume competence or you have to suggest that developers use identity proxies in the cloud, or frameworks others have written, and never implement this themselves.
But yes, this is a rather transparent advertisement for Redis as a KV session store.
- edgyquant 5y agoAren’t JWT tokens only supposed to be good for 10-15 minutes? I know using flask-jwt you have to go out of your way to make them last longer than that and it isn’t recommended.
- Grimm1 5y agoYes but a lot of times people don't do that properly, some frameworks have incorrect defaults or people don't want to deal with writing the logic to handle refreshing tokens after that 10-15 minute window etc etc. They are insecure but it's not a problem if you follow best practices like those, the difficulty is a lot of people don't because they don't really understand what it is they're doing or potentially causing by making the changes that let them be lazy or do things the easy way. You could probably change the title to articles like this from "JWT Tokens are NOT safe" to "JWT Tokens are NOT safe when you ignore all security practices and take the easy way out" but that doesn't make for a flashy title.
- lstamour 5y agoI know some folks who thought they could use JWTs from a traditional LDAP identity vendor (open source Active Directory so you can manage computer login credentials centrally, issue Kerberos tokens, etc.) but they managed to misconfigure their JWTs by following the defaults from the vendor so the JWTs issued were valid forever. When I tried to explain that they couldn’t be revoked without key rotation, I got some very blank looks. I’m not even sure the tokens had an iat (issued at timestamp) though if they did you could use that to at least ignore tokens older than a certain date. They assumed because they deleted the cookie at logout, it wouldn’t matter that the session could be re-used forever. Then they implemented session limits by setting a cookie expiry time. /face-palm Issuing your own JWTs also means you have to keep the secret or certificate used to sign them secure or if it leaks anyone could impersonate anyone else. This is especially problematic if admin or role assignment is embedded in the JWT.
- lstamour 5y agoExpiry is just one of the fields you can set. Also if you’re curious about what’s inside a JWT, the next time you see one from an OAuth cookie perhaps, copy and paste it into a tool like https://JWT.ms https://JWT.ms run by Microsoft and it can show you the details. Commonly you want to load a JWKS (with multiple keys possible for easy revocation) to validate the signature, and check the iss (issuer of the JWT), aud (your app registration or audience of the token) and expiry date of the token. Oh and don’t forget to hard-code or reference the JWKS validation method so you don’t accidentally allow “none”. JWTs are not without their complexity, but the same could be said about maintaining secure session cookies and implementing 2FA yourself perhaps.
- lstamour 5y agoI do want to follow up - there are two parts of JWT issuing that are problems and why I prefer using JWTs only issued by OAuth providers (third-parties that know what they’re doing) - 1. JWT tokens you issue yourself might not have expiry dates in them - I am assuming that your JWT tokens are valid for a reasonable duration such as 2 hours or less, otherwise you should probably use a different technology - and 2. Like SSL, you will have to rotate the credentials used to sign the JWT, otherwise anyone could pretend to be anyone else, at any time. So it’s not that JWTs don’t have risks but the risks are overstated. It would be like saying never use Redis because by default it doesn’t have secure SSL or a proper password system and thus anyone could access it. Security isn’t easy, but it can be done, and often looks like a series of mitigations, monitoring tools and trade offs… such as how long sessions last, or planning for features like key rotation or session revocation in advance…
- jakelazaroff 5y agoDon’t be so sure about the third-party auth providers. Even Auth0 accidentally allowed token validation bypass by specifying a weird capitalization of “none” for the algorithm. https://insomniasec.com/blog/auth0-jwt-validation-bypass https://insomniasec.com/blog/auth0-jwt-validation-bypass
- wokwokwok 5y ago> The general reason why folks don’t recommend JWTs is because it’s too easy to make mistakes in the validation logic. That isn't why I don't recommend JWTs, and it's not why this article is not recommending JWTs. > Logout doesn’t really log you out! > Blocking users doesn’t immediately block them. > Could have stale data ^ JWTs fundamentally are not compatible with server-side authentication revocation. Your typical developer is like, "Well, I don't care much, I'll use JWTs anyway", but then some poor soul has to deal with the ticket titled "Blocked user still able to access service", or "User still able to access service after logout", or "session never expires!" (oo... look, you configured it incorrectly). I've been on the other side of having to deal with screwed up JWT implementations other folk have built. It's not fun. ...and yes yes, there are work arounds, you can have rotating short lived tokens and long lived refresh tokens and a database of revoked tokens...but you've just reimplemented server side session authentication yourself, and it's probably wrong. > I think you have to either assume competence... I think that's terrible advice. You should never assume developers are competent to implement authentication. It's a Hard Problem, like, writing a database, and frankly, only specialists are competent to do this correctly. Anyone can writing some kind of data store, but it's a bit harder having it ACID and concurrently multiuser. > or you have to suggest that developers use identity proxies... ...but yes, this is probably the only useful piece of advice to give to people: If you use a 3rd party authentication provider, you can delegate responsibility for doing the hard work of making sure they have a solution for the hard things; at which point, you don't really care if its JWT or cookies, or whatever. I still wouldn't recommend people use JWTs.
- cameronh90 5y ago"database of revoked tokens...but you've just reimplemented server side session authentication" There's one important difference between session authentication and JWT+revocation: the revocation list can be distributed asynchronously, whereas a session list needs at least read-after-write consistency. It's a minor point for most use cases, but occasionally can be very significant.
- lstamour 5y agoThat said, the primary problem with distributed authentication and distributed revocation is either it slows down your processing as you wait for confirmation of new entries on the list, or you have a risk that actions might be allowed when another part of the system has tried to block them. We generally think of distributing authorization data asynchronously in the positive: adding a new user permission, rolling the list out to every authz server. But when you’re trying to block access immediately, it can be difficult. Not to be negative—if your JWT expires after 15 minutes and your list updates every 10 seconds, you’ve improved your reaction time quite a bit for revocations. Also, you can have an eventually consistent session store if you don’t mind that sometimes users might see a 403 they shouldn’t, if your app can cover it up such as by requesting content from a different region or waiting a bit. Similar to the idea of using a load balancer to pin sessions to particular servers.
- zamalek 5y ago> You could have short-lived JWT tokens so it wouldn’t matter because they only last 5-10 minutes or less. The author argues that you can't revoke a JWT - suggesting that 30min is the usual default. To their point, if 30min is too long, then 10min is probably still too long. However, implying that 30min is too long for a token to remain stale suggests that you aren't really working at the scale that JWT was destined to address (Facebook, Twitter, etc.). If you really are the unfortunate soul trying to solve per-request authz, specifically at the scale that JWT was designed for, then I feel deeply sorry for you. For the 99%, i.e. the rest of us, 30min is just fine. > You could use Redis for session data as pointed out with the random key in a cookie and delete the session when done to invalidate the cookie. I'm not picking at your argument, honestly, I'm just pointing out the absurdity of authz without JWT (or similar) at scale. Redis is eventually consistent: what happens during a network partition? Saying that the JWT expiry is a vulnerability is tilting at windmills. > The general reason why folks don’t recommend JWTs is because it’s too easy to make mistakes in the validation logic. Bravo, that's the real problem with JWT right there: it's too easy to misuse - especially when convenience or demanding customers enter the picture. It also has brain-dead specification opportunities like signature-free tokens.
- rexer 5y agoCould you expand on why 30min session validity after explicit logout is okay? I don’t mean to sound accusatory; I would like to understand your reasoning.
- lstamour 5y agoIf the JWT is saved as a cookie, you can delete the cookie and the user’s browser is safely logged out. The threat model is that a user or third-party could intercept and reuse the JWT after logout. Sure. But then a malicious actor could re-use a JWT before you logout from the app also, which is a much larger risk. Malicious browser extensions for example could hide that they’re making clicks or taking actions in tabs just as they hide ads from you. Don’t get me wrong, extensions are sandboxed, but… any sandbox can be broken. In the end, whether or not your JWT was revoked at logout doesn’t affect the risk of malicious activity all that much as long as cookies behave the way they should. And as long as your JWT has appropriate expiry timestamps.
- btown 5y agoI suppose if you really want to avoid the hop to session storage as much as possible, you could have your applications servers continuously refresh a lightweight Bloom filter or other probabilistic data structure storing maybe-invalidated sessions in memory, and in case of a potential hit, confirm it with the session store. Then your logout endpoint just needs to wait until all servers receive the data to tell you you're logged out (I suppose CAP applies here). If this sounds like overkill to anyone reading this, then it probably is!