[HN Gopher] Why to not use JWT (2021)
___________________________________________________________________
Why to not use JWT (2021)
Author : thunderbong
Score : 133 points
Date : 2022-09-29 12:59 UTC (10 hours ago)
(HTM) web link (apibakery.com)
(TXT) w3m dump (apibakery.com)
| matt74827289 wrote:
| I just use both. Use randomly generated tokens that you use to
| look up user information in your database, but also sign that
| token and present it as a JWT.
| kerblang wrote:
| The JWT is irrevocable by design, which is why it's time-boxed to
| a few minutes and not a few days. The refresh token is meant to
| be revocable. You the provider would store that in a database and
| remove it or mark it invalid when you need to shut the user out.
| So no, it is _not_ simply moving the original problem around
| without solving it.
|
| Most software devs using cookies would likely struggle to revoke
| an individual cookie - you can certainly architect that in, but
| if you're like most of us, nobody thought about it early on and
| now it's kinda problematic to retrofit. Same reasoning: "Well, it
| will expire on its own soon enough." JWT's actually make it
| easier to guarantee expiration since it's part of the token
| itself instead of being reliant on server-side mechanisms.
|
| It's definitely a pain in the butt to juggle refresh tokens &
| access tokens as a client-side dev, and almost everyone's API
| docs are ambiguous and confusing about token lifetimes (refresh
| tokens often have lifetimes too). But that's another issue.
| P5fRxh5kUvp2th wrote:
| refresh tokens are a workaround for a problem that wouldn't
| exist if you chose not to use a JWT.
| kerblang wrote:
| Again: If we decide to use cookies instead, we also need to
| architect in a Cookie Revoker to fix the same problem: That
| means tracking cookies against user accounts on the server
| side. I'm sure _some_ folks have done this, but I don 't know
| of any common frameworks that actually support it.
| P5fRxh5kUvp2th wrote:
| Or you can develop like it's 1999 and simply remove the
| session from lookup.
| quesera wrote:
| I have happily used JWTs as signed objects that can be easily
| parsed and validated by any API consumer, in any language, using
| library code.
|
| These objects can be transferred through untrusted channels, and
| retain their verifiability. That can be useful when moving data
| between organizations, or systems that do not talk directly (e.g.
| a mobile app that interacts with multiple unrelated backends).
|
| RFC 7519 says: JSON Web Token (JWT) is a compact,
| URL-safe means of representing claims to be transferred
| between two parties.
|
| Not all claims are authorization claims. Not all claims require
| ad hoc invalidation. Some claims can even be permanent!
|
| Authors ad infinitum have pointed out the risks in using JWTs for
| authorization (linked web article says "authentication", but both
| could apply).
|
| Those risks are: - Invalidation/expiration
| controls are limited - Receiver might not properly validate
| signature - Protocol dumbly allows "none" algorithm
|
| Only the first is an operational concern, the others are just
| "bad code works badly" problems.
|
| The moral of the story is: If your claims do not fit the timed-
| expiration model of JWTs (e.g. some authorization claims), then
| don't use JWTs!
| marcosdumay wrote:
| > the others are just "bad code works badly" problems
|
| Software should be hard to misuse. If you have a library
| intended for universal usage, and the library users have to
| remember to add some code every time they call it, then it's a
| bad library.
|
| If you have an specification intended for universal usage, and
| the implementers have to remember to always do 3 operations
| together, than it's a bad specification.
| kreetx wrote:
| Right, but rm -fr / is also easy to write, but is pretty bad
| code. JWTs and statelessness have their uses, building in
| refreshing is not that difficult (if it's not built in
| already), and using "none" well, just don't use that.
| marcosdumay wrote:
| There are alternatives that won't accept "none" or broken
| encryption algorithms, and that won't allow you to access
| the token data without verifying its authenticity.
|
| JWT is just a bad standard all around.
| 0x457 wrote:
| Many issues of JWT are actually issues on library for
| nodejs to work with JWTs and partially the language:
| trick server to use public key as secret to validate JWT,
| trick server to use none.
|
| JWT itself is also poor by having things like "none" at
| all. Which means in every valid place to use JWT, you
| should really be using PASETO.
| ragona wrote:
| You're missing the core challenge of JWTs, which is revocation.
| If you just check the signature then users can't log out.
| quesera wrote:
| Revocation == invalidation.
|
| User-initiated log out is easy, you just clear the token.
| Promiscuous token gathering is harder (but SSL..!) and makes
| the case for invalidation or short expiration periods. And
| forced logout is the legit operational problem case.
|
| Again though. If JWT does not fit your authnz model, don't
| use it. JWT works well in many applications outside of
| authnz, and some inside it as well.
| caeril wrote:
| This is a good overview, but I have yet to hear a compelling
| reason _to use_ JWT.
|
| String session tokens generated by a CSPRNG and stored in a
| cookie work fine for every use-case I can think of. What
| specifically is the argument _for_ JWTs?
| dzikimarian wrote:
| We have single SPA as front for multiple backend apps (I
| wouldn't call them micro services - each is responsible for
| pretty big part of the business and maintained by separate
| team). There's single identity provider responsible for token
| generation, sign in, refreshing the token every few minutes.
|
| It's easier to have for eg. userId embedded inside signed
| token. Nobody has to call identity provider, they don't even
| care from where did the tokens came from.
|
| Our threat model for this software doesn't care too much if
| token is valid for some seconds after logout. At this point
| user was probably MITMed or lost control over hardware and has
| much bigger issues than us.
|
| Could we do this differently? Probably yes. Would there be a
| positive impact for the company if we dedicated time to this?
| Very doubtful.
|
| JWT is only a tool. Sometimes it fits, sometimes not. No reason
| to get religious over this.
| datavirtue wrote:
| Hmmm...put the expiration datetime in the token and have your
| middleware handle it (verify and check for expiration)?
|
| Failing to see a problem.
| jdthedisciple wrote:
| That's already done.
|
| The problem is that apart from expiration, the token is never
| invalidated, for example when the user logs out. An attacker
| who stole the JWT is still logged into the victim's account
| until expiration.
| themenomen wrote:
| Can't you keep a whitelist/blacklist of tokens in a memory cache
| like redis/memcached and go from there? As far as I know that is
| the standard practice to invalidate non expired sessions tokens.
| brunojppb wrote:
| But then you introduce a database-like dependency that
| potentially every micro service will need access to.
| themenomen wrote:
| Yes, that comes with its own caveats
| drinchev wrote:
| As explained in the article, if that's the case then you can't
| really trust the JWT anymore only for it's cryptographic
| signature and you rely on an internal store entry that makes
| the token valid / invalid.
|
| This makes no benefits as to bearer token or any random string
| that the server "knows" is a valid authenticated request via
| internal store, like a DB.
| eli wrote:
| why not just use a session token at that point?
| hunterb123 wrote:
| Because you're only storing the invalidated tokens and you're
| only storing them for the length of the token's lifetime
| (expire the redis key when the JWT expires)
|
| So instead of storing all session tokens indefinitely, you
| only store invalidated tokens for a short period of time.
| ravenstine wrote:
| Since when were session tokens ever a bottleneck? That's a
| problem I've never heard of on any scale. Yet again, JWT
| seems to be trying to solve a non-problem. Session tokens
| don't even have to be stored indefinitely. If a user isn't
| active for a period of time (even 30 days let's say) then
| the session token can be removed. Or, if memory truly was a
| problem for session tokens (not sure why honestly),
| transition tokens to storage after x amount of time and
| bring them back into memory when the user is active again.
| hunterb123 wrote:
| I simply explained the efficiency difference of the two
| setups.
|
| You're storing and checking a smaller subset when doing
| stateless + invalidation cache.
|
| Whether or not you need that memory optimization is up to
| you and your machines.
|
| Personally it's about the same effort to implement and I
| prefer stateless + simple redis cache for invalidations.
| marcosdumay wrote:
| Well, on the other hand, you will have to distribute that
| revocation list somehow, what does add some complexity to
| your code.
|
| If you are already distributing lists around your
| application servers (that impacts the format and top
| performance of the redis cache), then yes, it's quite
| simple.
| hunterb123 wrote:
| Redis is pretty easy to distribute out of the box.
|
| And most likely you'll want a simple k/v distributed
| cache for other things so it's no extra work.
| jayd16 wrote:
| It's quite common. If you have an outage where your
| sessions are cleared, you can hit issues where all your
| users DDOS your auth service. If you persist the sessions
| you have the same problem except they DDOS the DB.
|
| If you use JWT, users that get a new token are free to
| use your app without any further timeouts and reduce the
| load on the auth service. With a session system, the auth
| service reduces performance on the entire app.
| ravenstine wrote:
| > If you have an outage where your sessions are cleared
|
| Why would that even be allowed to happen in the first
| place? Sessions should always have some level of
| redundancy even if the primary retrieval is done in-
| memory. An outage would imply that an auth service is
| simply offline. Loss of sessions is a sign of
| catastrophic failure and possibly inadequate
| architecture.
|
| > you can hit issues where all your users DDOS your auth
| service
|
| Even in a case of a breach where all sessions _must_ be
| cleared, JWT is _one_ potential solution to mitigate
| DDOS. The others are to not have client-side code that
| blindly DDOSes your server and to have some level of DDOS
| protection in front of the rest of your architecture.
|
| Moreover, even if you solve this problem for
| authentication, there's no obvious reason why you can't
| end up in a similar situation if some other service in
| your architecture is down. With JWT, all you've done is
| take the one service that is fundamentally one of the
| least complicated (storing hashes in memory) and treated
| it as if it's a critical point of failure. Sure, it can
| be, but auth is also one of the easiest things to
| horizontally scale, distribute, and synchronize at a
| relatively low cost. In JWT world, you've gone from
| storing even just random session numbers and now have to
| manage encryption keys, TTL, and possibly invalidation
| records (nearly defeating the entire purpose). That
| leaves even more room for things to go wrong.
|
| > If you persist the sessions you have the same problem
| except they DDOS the DB.
|
| Again, so what? You're supposed to do things to mitigate
| DDOS on your services regardless of whether you're using
| JWT. If your auth service can be that easily DDOSed
| (presuming this is an average website and not Facebook
| scale), then it would only take some coordinated enemies
| with a bone to pick to DDOS you without even using legit
| JWTs.
| jayd16 wrote:
| >Why would that even be allowed to happen in the first
| place?
|
| Not just failure, you could also see this with just big
| spikes in traffic like a release day.
|
| Either way, sounds like you're convinced the cases are
| common enough that mitigations should be table stakes.
| adolph wrote:
| > Since when were session tokens ever a bottleneck?
|
| JWT allows for a user to authnz with a third party
| trusted by the second party. An example of this is HL7
| FHIR SMART app launch, where an outside web application
| (2nd party) is opened from within an electronic medical
| records system (3rd party).
|
| http://hl7.org/fhir/smart-app-launch/index.html
| eagsalazar2 wrote:
| This "the invalidated JWTs list is smaller" argument really
| doesn't hold water. Size of cache is a red herring (unless
| you are facebook or something). 99.99% of the time the most
| important variables are _simplicity_ and security, surface
| area for dev errors or attacks. Smaller invalid cache fails
| on those fronts and only is superior in ways that don 't
| actually matter.
| hunterb123 wrote:
| I answered those talking points here:
|
| https://news.ycombinator.com/item?id=33020993
|
| I disagree that stateful based sessions are simpler.
|
| I disagree size of cache is a red herring.
|
| You don't have to be FB to want to optimize your memory
| usage.
|
| In the end I prefer the more efficient (mostly stateless)
| solution and it's really easy to implement.
|
| But to each their own of course.
| jaimehrubiks wrote:
| Yes but if each microservice needs to check a cache on each
| request, then why use jwt at all, you could just save a classic
| session (with random token) in that cache as well
| wdb wrote:
| Why not let the API Gateway check it?
| eagsalazar2 wrote:
| "Yes but then ... why use jwt at all, you could just save a
| classic session (with random token) in that cache as well".
|
| JWTs add complexity and have zero benefit. The author never
| said it can't be made to work, obviously it can. It's just
| completely pointless in most applications.
| hunterb123 wrote:
| The redis cache with invalidated tokens will be much smaller
| than storing all sessions.
|
| And you can expire the invalidated keys faster (set the
| invalidated key expiration to the expiration of the JWT)
|
| Not many people revoke sessions, but a lot of people create
| sessions. Much more efficient to only store and check
| revocations. The rest can be stateless.
| szastamasta wrote:
| I don't think that biggest issue with central auth database
| is its size.
| moduspol wrote:
| It depends on your scale, but even at small scale, your
| central auth database is something that needs to be
| highly available because your whole system is down
| otherwise.
|
| Obviously the situation is the same when managing a token
| blacklist if you truly have a hard requirement that
| sessions be instantly invalidated at a specific point,
| but there's a good chance you don't. Maybe it's OK to
| presume signed tokens are still good for some amount of
| time if your blacklist server is unreachable, or maybe
| waiting until the JWT expires after 60 minutes is too
| long, but a one or two minute delay is acceptable. Or
| maybe you only check the blacklist for high-risk API
| requests.
|
| It's not ideal. Invalidation is definitely a weakness of
| JWTs, but there's still a lot of value in baseline
| statelessness.
| dementiapatent wrote:
| Yes, and there is even a JWT field to hold the unique
| identifier for that token - https://www.rfc-
| editor.org/rfc/rfc7519#section-4.1.7 .
|
| Makes it easy to track issued tokens and revoke them too.
| rkagerer wrote:
| That's discussed in the article in the second paragraph under
| "Problems with JWT". If you're keeping a cache at each server
| node then you might as well just use bearer tokens.
| vivekv wrote:
| I don't quite understand the need for access and session token as
| a mechanism of expiring sessions. Can we create a signed JWT with
| the expiration in the body itself? Since it is signed the expiry
| cannot be tampered with.
| sascha_sl wrote:
| If you mean access and refresh tokens - both usually have a
| lifetime in the signed payload.
|
| Your access/ID token is short-lived (~5 minutes). This token is
| trusted without confirming if the user still has access.
|
| Your refresh token has a longer lifetime (hours to months) and
| can be used to trade for another access token (and a new
| refresh token, invalidating the old one), but every time you do
| this trade your authentication server can also check if the
| user still exists, is not banned, has not signed out and still
| has the same claims (username, email, groups...) and either not
| issue a new token or a token with different claims.
|
| There are proxy servers that will do this entire thing in the
| background for you and hand you the claims of the current
| access token in HTTP headers.
| Benlights wrote:
| If there is an expiration in the JWT, logging off is still an
| issue. You can't log out until the JWT expires.
| drikerf wrote:
| Not saying you should use JWT, but you can invalidate tokens by
| adding a timestamp to the payload. This way you could invalidate
| those issued before a certain time and logout users.
| lucasyvas wrote:
| This is unnecessary - expiry time is a first-class capability
| of a JWT and records the time of expiry, not when it was
| created.
| drikerf wrote:
| Yes, but if you want to invalidate say currently logged in
| users, before the tokens expire.
| lucasyvas wrote:
| I suppose, but generally the solution is a refresh token
| with the JWT having a very short expiry to the point where
| adding a timestamp would be superfluous.
|
| IMO the only flexible way is a denylist of tokens or JTI
| which adds the burden of more infrastructure.
|
| My take is to use an opaque token unless you are using
| OAuth and paying someone else for all these extra
| capabilities, at which point things become easy.
|
| ex. If you are using Firebase Auth or Auth0, they manage
| the token revocation for you so the problem sort of melts
| away and you are left with only benefits of JWT. Just have
| to blacklist the JTI claim and it'll automatically be
| denied in the future.
| [deleted]
| ehutch79 wrote:
| The overwhelming majority of JWTs have an expires field. That's
| not the issue.
|
| The idea is that you have a disgruntled employee with a token
| that expires in 5-10 minutes. You don't have invalidation
| checks, so what damage can that employee do in 5-10 minutes?
| Keep in mind for many companies, exporting a client list is a
| big deal.
|
| Why would you not have validation checks? The point of a JWT is
| they're stateless. When you get one, if you have the key, you
| can validate it without access to the auth service.
|
| If you take your JWT, and then are using the claims to check
| the database, it is 100% functionally the same as a session
| token.
|
| JWTs are not something you issue to use on your own service.
| It's for another service to verify the user on their service
| based on a factor coming from your service.(yes i'm aware there
| are other uses)
|
| If the only thing in your payload is a session_id or a user_id.
| Stop using JWTs.
| drikerf wrote:
| That was my point. You can easily add validation checks to
| JWT. Just invalidate tokens issued before a certain time.
| senko wrote:
| How do you do it without the service going into some sort
| of a database to check if there's a certain time for which
| all tokens older than it should be invalidated?
| ffo wrote:
| Well you don't ;-)
|
| If you stick to OAuth and OIDC you have the option to
| validate the tokens against the userinfo and introspect
| endpoints, but that's, just another "database"
| tzs wrote:
| > The idea is that you have a disgruntled employee with a
| token that expires in 5-10 minutes.
|
| Assuming the tokens issued to employees have an N minute
| lifetime stop replacing expired tokens for that employee N
| minutes before you do whatever it is that might disgruntle
| them enough to make them try to trash your systems?
| the_sleaze9 wrote:
| > what damage can that employee do in 5-10 minutes?
|
| This is not an authentication issue (JWTs) this is a classic
| authorization issue (Permissions/Roles).
|
| It's not the authentication layer's fault if you allow
| everyone root access.
|
| JWTs are just fine. Bearer tokens are just fine. You can
| write shitty session code just as easily as shitty OAuth2
| code.
| ehutch79 wrote:
| To clarify, what I do for sso stuff, if I get the JWT,
| verify it, then generate a session_id to hand back to the
| frontend. I'm essentially using the JWT as a replacement
| for username/password when it comes from a sso provider. I
| can then do invalidation or deny login as normal.
| ehutch79 wrote:
| You're absolutely right, but then we reach the, "why are
| you using JWTs?" part of things. if you're issuing them for
| your own service because that's what some guy on reddit
| said you should be doing...
| jayd16 wrote:
| Why doesn't the same argument apply when looking at other
| services? Can't they still do a lot of damage in that 5-10
| minutes against those services?
| ehutch79 wrote:
| You're not wrong.
|
| I replied to a sibling comment. What I do is use the JWT
| from oauth or whatever sso, verify it, and log the user in
| as normal. Using the JWT as a replacement for a
| username/password.
|
| I can invalidate the session or block the user as normal.
| newbieuser wrote:
| why is a blog post that directly praises its own product for
| short reasons on the homepage?
| brunick wrote:
| why is this not the top comment...
| eagsalazar2 wrote:
| Because the content is also interesting, provocative, and 100%
| correct. Why are you attacking the character of the witness
| instead of the substance of the testimony?
| jdthedisciple wrote:
| Lack of invalidation ("out of the box") is indeed a weakness of
| JWT.
|
| However, I think in some scenarios it doesn't matter. For example
| if all the user data is encrypted, an attacker can retrieve it
| from the server using the JWT but not decrypt it - how useless!
|
| Besides, when it comes to criticisms like this, I would gladly
| like to be pointed to real-life incidents where JWT as the
| security-bottleneck was fatally exploited.
| brunojppb wrote:
| This is interesting because one thing people ask me when a system
| is using JWT is in case they reset their password, how can they
| invalidate all existing sessions? They basically can't.
|
| Using A cookie-based/token-based session strategy, you can do
| that quite easily.
| cas8 wrote:
| It's pretty simple to check a JWT's issued time again a
| timestamp representing the last time an account changed
| password / revoked all sessions.
| cogman10 wrote:
| JWT is a token, often stored as a cookie. I don't see how it's
| impossible to invalidate a JWT but easy to invalidate a cookie
| or token.
| zaphar wrote:
| JWT is a token, often stored as a cookie.
|
| This is both true and also misses the point. Session Cookies
| are ususally stateful. In other words, the server validates
| the session by querying a service or database. JWTs however
| are stateless. You don't query a service or database to
| validate the service. As a consequence if the JWT is not
| expired and is still using a valid key there is no out of the
| box way to invalidate the JWT. You can build custom
| invalidation measures on top of JWT but it's not a natural
| consequence of the design in the same way that a session
| cookie is.
| Justin_K wrote:
| If you are using a cookie and managing the session expiration
| on the server, you can kill it anytime.
| cogman10 wrote:
| Throw a session ID in the JWT and do the same thing.
|
| JWT has a freeform payload that can literally be anything
| you want. Nothing about it prevents you from using a
| pattern like this.
| DavidSharff wrote:
| You can track JWTs on the server, via a sessionId or
| whatever you wish, it just breaks the intended pattern.
| If you have to do a lookup on each request (necessary to
| invalidate the token imperatively) your JWT is no longer
| stateless which is a core tenet of the JWT approach.
|
| It'd be like building a React app and calling
| getElementById(id) to update DOM values. You _can_ do it
| but...
| cogman10 wrote:
| > breaks the intended pattern.
|
| Who's intended pattern? Where is this stated as being the
| "right" way to use JWTs?
|
| I'm seeing a lot of claims about the intent behind JWTs
| but frankly I think it's because people are skipping over
| having a fundamental understanding about WHAT JWTs are
| and instead are cargo culting on what they believe they
| should be.
| DavidSharff wrote:
| My perspective is that I had recently realized that I
| didn't have a great justification for having used JWTs in
| my last two projects (and worried I had been part of a
| cargo cult myself). Truly.
|
| I can't see their value against a bearer token + session
| tracking on the server for most cases (e.g. it won't be a
| huge performance hit to do a lookup of some sort on each
| request).
|
| The two apps I'm referring to have a few thousand users
| who only make occasional requests.
|
| I think a lot of apps fall into this broad category and I
| don't see what extra value JWT is providing. Encoding
| user data is pretty convenient (though more opaque) but
| if you want to be able to ad-hoc invalidate them you need
| refresh tokens or a session list. Not only does that re-
| introduce needing to do a sort of lookup and server user
| tracking, the encoded data on the token is no longer a
| positive, since you bifurcated knowledge of the user
| (token + list), and all its data would be more
| discoverable by including it where you are now tracking
| sessions anyways.
|
| Help me out if I'm missing something. My mind is open.
| zaphar wrote:
| If you do this you get 0 value from using a JWT over a
| regular session id. It's simpler and likely less work to
| just not use a JWT in that case.
| cogman10 wrote:
| The value add is you don't need a cryptographically
| secure session id generation mechanism when you work with
| JWTs.
|
| A simple session ID can be (and often was) forged to
| grant access to things end users shouldn't have. Put the
| session id in the JWT and now an attacker can't easily
| change the session they are associated with.
| zaphar wrote:
| generating a sufficiently random session id is not
| difficult. Using JWT doesn't substantially make this
| easier for you and operationally you are going to have to
| invest in significant key management infrastructure. If
| you don't need the stateless nature of JWT you should
| just not use them.
| cogman10 wrote:
| > generating a sufficiently random session id is not
| difficult
|
| There are enough CVEs on this specific topic that I tend
| to disagree. I've been around on the internet long enough
| to witness a HUGE number of session hijacking attacks.
| [1]
|
| > Using JWT doesn't substantially make this easier for
| you and operationally you are going to have to invest in
| significant key management infrastructure.
|
| IDK about making it easier, but rather using JWTs for
| this makes it harder to get wrong.
|
| > If you don't need the stateless nature of JWT you
| should just not use them.
|
| Agreed. Use them when they make sense and not when they
| don't. I'm not arguing that JWTs are panacea. They are
| neither good nor bad, just a tool.
|
| [1] https://owasp.org/www-
| community/attacks/Session_hijacking_at...
| dns_snek wrote:
| > I've been around on the internet long enough to witness
| a HUGE number of session hijacking attacks.
|
| JWTs don't make it any harder to hijack sessions, in
| fact, they often make it easier.
|
| Session sniffing and man in the middle attacks don't
| discriminate between JWT and cookies/session IDs.
| Nowadays they are very hard to achieve with the vast
| majority of the web operating over HTTPS with optional
| HSTS.
|
| Cookies containing the session ID can be marked as HTTP
| only, unlike JWTs which are often stored in localStorage,
| which makes them vulnerable to extraction via XSS
| vulnerabilities.
|
| > using JWTs for this makes it harder to get wrong
|
| Any popular web framework these days should provide
| secure built-in functions to generate and validate
| cryptographically secure signed cookies containing the
| session ID.
|
| JWT libraries have had critical vulnerabilities in the
| past, such as allowing usage of the badly designed "alg:
| none" feature of the JWT specification.
|
| https://auth0.com/blog/critical-vulnerabilities-in-json-
| web-...
| zaphar wrote:
| JWTs have been in the news a _lot_ for trivial
| vulnerabilities and among security /cryptography
| professionals is widely regarded as a problematic
| standard. I would argue just as you seem to be that the
| surface area for ways to get them wrong is much worse
| than session tokens.
| xorcist wrote:
| You do need a cryptographically secure RNG to work with
| JWTs though. There's no getting around the need for
| randomness.
| palunon wrote:
| If you treat the JWT as a random token and check whether it
| is valid in your central database every time, then yes, it
| behave like a random token and you can invalidate it very
| easily (just remove it from the list of valid tokens). But
| then, why use a JWT instead of a random token?
|
| If you treat like it is supposed to be treated and check the
| cryptographic signature instead of going to a central
| database, then you can't invalidate it (at least before it
| expires). You can ask the client to remove it, but what if
| they don't? (eg. they are an attacker)
| cogman10 wrote:
| JWT is a token that is hard to forge with a payload.
|
| Meaning, if someone gives you a valid JWT that says "I'm
| user 123" you can trust that this is user 123.
|
| That's all it is. When reading in intent on how it should
| be used or what you should do with it, that's something
| beyond what the thing actually is.
|
| So any pattern you can imagine with a session ID, you could
| implement with a JWT. Just throw the session ID in as part
| of the payload. Just because you are using JWTs doesn't
| mean that you don't have to work with any sort of external
| resource while you are processing it.
|
| What purpose does it serve then? You can still avoid
| lookups when a JWT makes a claim. You know the JWT is valid
| so you know that all claims made within it are true. If it
| claims "I'm user 123" then they are user 123. If it claims
| "My account number is 435" then that's the account number.
| You don't have to make a lookup to see those facts are
| true. If it says "My session is 433" then you can validate
| that session 433 is still active and act accordingly.
|
| JWT isn't a panacea but it also isn't anything more than a
| bag of claims. It doesn't claim to be anything else either.
| xorcist wrote:
| > Just throw the session ID in as part of the payload
|
| An authenticated session id is just a very very long
| session id.
| senko wrote:
| > If it claims "My account number is 435" then that's the
| account number.
|
| What if the account number changed since the token was
| created?
|
| This works only for immutable claims. If the truth behind
| those claims changes in the mean time, you have to be
| able to invalidate the token, or accept the fact that it
| serves stale information.
| cogman10 wrote:
| > If the truth behind those claims changes in the mean
| time, you have to be able to invalidate the token, or
| accept the fact that it serves stale information.
|
| Isn't that obvious? I mean, if we were talking about
| sessions and you put in the session set information that
| can change outside the session, wouldn't the same problem
| exist?
|
| I just don't see this as a fundamentally unique problem
| for JWTs.
| bhandziuk wrote:
| If the password is changed then the encryption on the JWT is no
| longer valid though. So it is unusable at that point.
| vasachi wrote:
| What? Why whould the signaure be invalidated?
| b3morales wrote:
| Not the cryptographic signature; they mean the
| authorization.
|
| I.e. if the user's password has been compromised, then the
| attacker with the password could have started a new session
| using it. The question is, how do you (allow the user to)
| revoke the auth that was granted by the compromised
| password and invalidate that fraudulent session.
| tylergetsay wrote:
| The users password isnt involved with the encryption of JWTs
| bhandziuk wrote:
| If the user's password isn't involved then what's the
| mechanism for invalidating existing tokens?
| ffo wrote:
| One could include the SID claim, with this the token at
| least could be tested against a users session on the
| IdP/OP. (This is being used in the ID_Token in OIDC)
| Sohcahtoa82 wrote:
| There is none.
|
| And now you're starting to understand why JWT's aren't
| worthy of the hype. A truly stateless JWT implementation
| is insecure since you can't invalidate existing tokens.
|
| The usual compromise is make JWTs have a short (<= 15
| min) expiration and provide the client with a refresh
| token that doesn't expire, but is stored server-side.
| When the user logs out, the refresh token is invalidated
| server-side so a new JWT can't be issued. You're re-
| introducing state with this solution, but you're making
| it so you don't have to bang on a database with EVERY
| request, just one every ~15 min or whatever your
| expiration window is.
| super256 wrote:
| There is none. Personally, I use it for authentication on
| a platform where the user shall be logged out after 30
| min anyway.
| frankthedog wrote:
| I believe GP is referring to user password not the encryption
| key. Rotating the encryption key would invalidate all
| previously minted JWTs. A user changing their password would
| not because it is not used to sign the JWT, therefore the old
| JWTs would still be valid until expiry after a user changes
| their password.
| bhandziuk wrote:
| It's my understanding that user's password + a server
| secret = the encryption key. Changing either one of those
| will invalidate the token.
| ehutch79 wrote:
| Why in the fresh hells would you store a users password
| in plain text, in order to decrypt that key?
|
| Even if you didn't you still need to retrieve the user
| and password from some storage to validate the key, which
| invalidates the reason for JWTs in the first place, since
| you supposed to be able to validate them without access
| to an auth service/db
| bhandziuk wrote:
| Plain text?? A user's password is hashed...You don't need
| a paint text password to compare text encrypted with it
| to other text encrypted in the same method. Either they
| match or they don't. The user's hashed password doesn't
| leave the server and plain text is never involved.
| b3morales wrote:
| If you use the password's _hash_ as a key, the plain text
| no longer matters, because the hash is now the thing that
| an attacker needs to forge a credential. So your database
| is effectively storing the real password, as if you had
| not used a hash.
| bhandziuk wrote:
| I think I see what you're saying but how would that be
| useful still? Like is an attacker has a hash of the users
| password they still don't have the server secret.
| dns_snek wrote:
| Having to look up the dynamic signing key reintroduces a
| trip to the database. At that point it's nothing more
| than a session ID with extra steps.
| anonymous344 wrote:
| Great article. I was going to start finally using jwt:s but this
| helps me not to. Currently using sessions (fast & easy) and if
| not found, then cookie&db-login. Just after migrating the server
| found out that saving the sessions manually to database was
| great. That allowed all users to stay logged in while the server
| changed.
| pwinnski wrote:
| > An old joke is that there are only two hard things in computer
| science: cache validation, and naming things.
|
| Way to _ruin_ the joke! The joke is that that are only two hard
| things in computer science: cache invalidation, naming things,
| and off-by-one errors.
|
| See, now it's a joke.
| atleta wrote:
| I wouldn't say he ruined _the_ joke. The original saying quite
| obviously only covered the first two issues. The off-by-one
| error was only added later. Though it definitely did enhance
| the joke factor.
|
| https://www.karlton.org/2017/12/naming-things-hard/
| ysavir wrote:
| But without the line about off-by-one errors it isn't a joke,
| just a straight forward statement.
| jacobolus wrote:
| The joke is that these two things seem trivial (perhaps
| even outside the scope of the real "problem solving" parts
| of the job) and computing is full of apparently much
| trickier problems. But in practice everyone repeatedly runs
| into trouble with the same couple basic issues.
|
| It's a joke because it's not literally true - the other
| problems in computing can be incredibly hard, sometimes
| taking years of work to figure out - but like any good joke
| it does also have a kernel of truth and hopefully makes the
| listener do some reflection.
|
| Adding "off by one errors" is a riff which only makes sense
| once people are already familiar with the original joke. It
| can be funny but also kind of misses/obscures the original
| point.
| Ancapistani wrote:
| No - there are _three_ hard things in computer science: cache
| invalidation, naming thi, concurrencyngs, and off-by-one
| errors.
| cmpb wrote:
| I really like this version of the joke, but it's impossible
| to tell during regular face-to-face conversation
| kroolik wrote:
| You can pronounce it with the cardinals
|
| 1st Cache invalidation
|
| 3rd Concurrency
|
| 2nd Naming things
|
| 4th Off-by-one errors
| phendrenad2 wrote:
| Cache invalidation, concurrency (say slowly), naming
| things, off-by-one errors, and concurrency!
| lcnPylGDnU4H9OF wrote:
| That could work but it would be easy to get the timing
| wrong.
|
| Also, nit: those are ordinals (since they specify the
| order). Cardinals would be one, two, three, four.
| Sohcahtoa82 wrote:
| Two threads walk into a bar. The bartender says, "I don't
| race want any like conditions time last!"
| runiq wrote:
| And scope creep!
| eckesicle wrote:
| and exactly once delivery, and exactly once delivery.
| MAGZine wrote:
| it was in the original joke.
| pjmorris wrote:
| I would tell you a UDP joke but you probably won't get it.
| classified wrote:
| I could tell you a mathematical joke but that would be
| going off on a tangent.
| Gordonjcp wrote:
| I heard a good source routing joke but I don't know who to
| tell it to next
| gorkish wrote:
| I'm sorry. I missed that. Can you please tell me again?
| remram wrote:
| If you add anything else, you ruin the "off-by-one" part.
| tmccrary55 wrote:
| Then there are three hard things
| drinchev wrote:
| What I'm pissed about is that everyone hopped on the JWT train
| too fast a couple of years ago.
|
| Now it's really hard to argue with architects / developers why
| cookie authentication / bearer token makes more sense than JWTs.
| ravenstine wrote:
| JWTs aren't even hard to argue against in terms of logic. What
| is hard to argue against are _developers_ whose theology
| includes JWT.
| super256 wrote:
| How would you handle user authentication on a serverless
| platform? JWT are imo perfect for that since they are
| stateless.
| xorcist wrote:
| How do you handle logouts?
| dns_snek wrote:
| I'm not the person you replied to, but in most
| implementations I've seen - they don't. Front end or app is
| politely asked to delete the token to simulate the user
| logging out, but the token isn't revoked in any meaningful
| fashion.
|
| No additional checks are performed on the back-end to
| verify whether the token has been revoked as that would
| reintroduce a round trip to the database you're trying to
| avoid in the first place.
| ffo wrote:
| Well, when dealing with OIDC / OAuth you can bind the
| tokens to the user session or trigger back channel logouts.
| But anyways its not really easy to tell an RP to stop using
| a token.
| 9dev wrote:
| Issue tokens that have a reasonably short TTL - say, half
| an hour - and let clients use their refresh token to obtain
| a new token after that. On refresh requests, ask the
| database whether the refresh token has been invalidated, if
| it is, return 403 (bonus points for checking the expiration
| date first and delete expired _and_ invalidated tokens from
| the invalid-list).
|
| This reduces the necessary database roundtrips, while still
| supporting a logout flow.
| ryeguy wrote:
| This also means an attacker can be running around with a
| compromised token for up to a half hour before they're
| stopped.
| 9dev wrote:
| If that is unacceptable for the business case, it's
| probably clear JWTs for sessions are unfit for the
| particular task?
| fknorangesite wrote:
| No one - OP included - is arguing that there are no good use
| cases at all. Just that most situations don't call for it,
| and you're better off with something less complex.
| cogman10 wrote:
| > Now it's really hard to argue with architects / developers
| why cookie authentication / bearer token makes more sense than
| JWTs.
|
| Because that's a nonsensical argument? JWT is just a token +
| validation. Nothing more. You can use JWTs in cookie
| authentication, you can use them as bearer tokens. The only
| thing JWTs are doing is carrying a payload and signing it.
|
| Now, if you want to talk about Oath2 or OIDC then maybe there's
| a different argument to be had.
| sbergot wrote:
| Why would anyone use a jwt in a cookie? A sensitive cookie
| should be encrypted. It does not require any signature.
| jaywalk wrote:
| Because cookies can be set to HttpOnly, making them
| inaccessible to JavaScript. They're also automatically
| included in all requests, so if you want to download a file
| that requires authentication, you don't have to do some
| convoluted JavaScript trickery to accomplish it. It can
| just be a regular link.
|
| I feel like the people who bash JWTs have never actually
| built a real-life application using them. Yes, there are
| footguns. But they are dead-simple to mitigate, and they
| are _far_ from the only footguns in the world of web
| applications.
|
| Yes, you can't fully "log out" without a centralized
| database. But who cares? A JWT in an HttpOnly and Secure
| (requires HTTPS) cookie is very well locked-down. I'm not
| worried about an attacker being able to retrieve it,
| because if they can then the client is pretty well owned at
| that point and the attacker can do whatever they want.
| cercatrova wrote:
| But why not use a session cookie also with HttpOnly?
| cogman10 wrote:
| I feel like this is mixing concepts.
|
| JWTs aren't for transmitting sensitive data or encrypting
| things. JWTs are about having claims that can't be forged
| by the client.
|
| So why would you put a JWT in a cookie? To give the end
| user a set of claims that they can't change, they can only
| read and give back to the server. Those claims can include
| things like user id, or session id, or whatever you might
| imagine.
|
| Now, could you accomplish the same thing with an encrypted
| cookie? Absolutely. I'm not arguing about what you can or
| can't do with stuff. But rather again commenters saying
| "JWT is such and such and forces so and so". It's nothing
| but a signed set of claims.
| quesera wrote:
| JWTs can be encrypted and signed, or just signed (or
| signed alg: none).
| cogman10 wrote:
| Ah, didn't realize encryption had made it into the spec.
| P5fRxh5kUvp2th wrote:
| stop being snarky, there are RFC's describing JWS and
| JWE, people just use the term JWT generically and let the
| context dictate what they're actually referring to.
| cogman10 wrote:
| You are reading my comment uncharitably.
|
| I did not know that encryption had made it in the JWT
| spec. My comment was supposed to convey surprise, not
| some sort of snark or sarcasm. I read the spec after the
| OP comment and learned something new today.
| marcosdumay wrote:
| > Now, if you want to talk about Oath2 or OIDC then maybe
| there's a different argument to be had.
|
| I imagine that is the main argument. People use JWT because
| it's standardized on the authentication protocol... The same
| authentication protocols that are horrible in many more ways
| than simply using a bad token format.
|
| Yet everybody jumped into them when Google commanded.
| jmartrican wrote:
| I agree. Not enough critical thinking was happening when I saw
| devs start adopting JWT without clearly stating why, other than
| "current best practices is to use JWT... end of discussion".
|
| My concerns with JWT from early on is that the data stored in
| them was potentially stale. Front-end developers would always
| request fresh data at each interaction. Second, the JWTs were
| so long. We had to keep passing these long JWTs around....
| mainly for testing stuff out, we had long lived tokens,
| especially in dev, so I think we passed them around to
| replicate API calls. So you felt how long they were.... and in
| my head I kept thinking about all this useless data being
| passed around taking up CPU/network/memory resources. So I
| would just remove JWT and replace the tokens with UUIDs.
| Everyone was happy about it, but they were confused as to why
| they were needed in the first place. I would just respond with,
| well when you find out let me know and I can add them back.
| solatic wrote:
| Yep. Not everybody runs at FAANG scale.
|
| Have a B2B product where you'll have, maybe, someday, and I'm
| exaggerating here, X00,000 DAUs? Just set up cookie auth and
| track the sessions in Redis. Session revocation is super-super
| simple and you'll easily be able to handle any security vendor
| questionnaire asking how you lock out terminated accounts.
| wk_end wrote:
| As someone who's a little behind the times here, can someone
| explain the distinction between a session ID and a token? I'm
| referring to this passage:
|
| > Sessions were built on top of cookies. The server kept context
| about the user's "session" [...] and indexed it with a random
| session ID. The session ID was sent as a cookie and for each
| subsequent request, server looked up the session state.
|
| > Then came mobile apps and single-page JavaScript apps, and
| plain cookies and sessions started being inadequate. Developers
| started using tokens, where a token was an unguessable random
| string that behaved like an identity card. Possession of it
| proved your (client's) identity (bearer token).
|
| Both a session ID and a token are random strings clients send
| with requests to indicate who they are. Why were sessions
| inadequate, and how do tokens solve this problem?
| adolph wrote:
| > Both a session ID and a token are random strings clients send
| with requests to indicate who they are. Why were sessions
| inadequate, and how do tokens solve this problem?
|
| JWT tokens are not random strings. They contain claims about
| the bearer that are cryptographically signed to prevent the
| bearer from altering them.
|
| https://jwt.io/introduction
| stefs wrote:
| yes, but they ask about the difference between session ids
| and bearer tokens (NOT jwt).
| ehutch79 wrote:
| There's nothing actually wrong with session ids in a cookie.
| It's that JWT was the new hotness and a buzzword. Now there's a
| lot of cargo culting around them.
|
| JWTs are a signed claim that your service can trust, and use
| for authorization after just verifying the signature.
|
| A session id, you need to take the id and look up your session
| in some mechanism on your backend.
|
| JWTs are great for different services saying 'yes, this
| connection is actually this person, trust them.' Inter-server
| communication, oauth, etc, are good examples.
|
| Session ids only work for the issuing service. Django sessions,
| php sessions, etc, are good examples.
|
| One of the biggest problems with JWTs is that a lot of people
| are ONLY storing a user_id, or a session_id in them. On the
| server the decode the token and proceed with the standard
| session_id workflow, still hitting the db.
|
| Invalidation is another thing. You still end up hitting the
| database, and if the JWT is only ever used by your own service,
| why bother?
|
| Then there's people just shooting themselves in the foot and
| not actually signing them and just trusting them without any
| crypto signatures.
| newaccount2021 wrote:
| xtracto wrote:
| >Then there's people just shooting themselves in the foot and
| not actually signing them and just trusting them without any
| crypto signatures.
|
| You would be surprised, a very short time ago I saw a (pretty
| large/common) web FinTech service provider that included the
| _private_ key to verify the signature encoded within the JWT.
| It was SHOUTING to be hacked. I don 't understad how people
| can be so irresponsible.
| Salgat wrote:
| You mean the public key? The private key is what's used to
| sign tokens, the public key is used to verify the signature
| and can be freely shared. Or maybe you mean they used a
| symmetric key and shared that?
| xtracto wrote:
| Private key to SIGN the content.
| P5fRxh5kUvp2th wrote:
| what am I missing?
|
| a JWT signature is the header and payload encrypted and
| attached. You would do that encryption via a private key,
| said public key would be used to verify it was encrypted
| by the private key.
|
| What does "to SIGN" mean in this context?
| ehutch79 wrote:
| I'm pretty sure he meant they were putting the private
| key in the jwt.
|
| People do some dumb stuff. And the dunning kruger effect
| is all too real.
| Salgat wrote:
| He said it was used to verify the token, which is only
| true if you use a symmetric key not a private key.
| ehutch79 wrote:
| Because they got the code our of a reddit comment?
| andrewstuart2 wrote:
| The "problem" is that JWTs are just a container format.
| There's nothing saying they even have to be signed, though
| they're designed to be more easily signed or encryptable.
| I've implemented the ridiculous SAML enveloped
| encryption/signature standard and canonicalization, and
| honestly, the simplicity of JWT while still providing for
| resilient security is a fine reason for its popularity when
| compared to some of the alternatives for federated
| id/authentication.
|
| The right thing to do is use web standards (like OIDC) and
| libraries and existing tools (keycloak, ory suite, et al),
| same as with cookies. You can do the wrong things with both
| JWTs and cookies, and any other client-identifying
| technology.
| prpl wrote:
| not sure it is new hotness anymore. Maybe in 2016, when it
| was still several years old and people still had tons of
| issues with it.
| stefs wrote:
| note: most other answers compare sessions to JWTs, not the
| bearer token. i'm guessing you're asking about sessions vs.
| bearer tokens, not sessions vs. JWT.
|
| ----
|
| i've been wondering the same thing. the only explanation i have
| are life times and explicitness. sessions are _usually_ based
| on the browser session, i.e. cookies without an explicit
| expiration date are invalidated if the browser is closed. also,
| session cookies are always sent on any request (simplification)
| to the matching hosts. this means you have to re-login to get a
| fresh session every time you reopen the browser (unless you set
| an expiration date for the session cookie i guess).
|
| i guess bearer tokens have a very long life time independent
| from the browser session and the user can invalidate them
| manually through some settings option. moreover, the token is
| added to the relevant backend calls manually (instead of
| automatically by the browser).
|
| i'm not sure though how a bearer token would prevent CSRF. imo
| it's roughly the same as a session with a long expiration time.
| P5fRxh5kUvp2th wrote:
| bearer tokens are tokens passed via the http authorization
| header as a bearer scheme.
|
| That's it. That's all they are. If they're not passed in that
| manner, they are not a bearer token.
| jdmichal wrote:
| > i'm not sure though how a bearer token would prevent CSRF.
| imo it's roughly the same as a session with a long expiration
| time.
|
| You said it yourself. Bearer tokens have to be added
| manually, while cookies are automatic. So simply issuing a
| request to a backend will not include the bearer token, and
| you (as the attacker) have no way of actually gaining that
| token from your cross-site context.
| stefs wrote:
| as far as i understand, that's not necessarily the case; a
| malicious CSRF triggers a completely valid request by
| tricking the user to open an url while logged in. imo the
| principle works for both sessions and token based auth as
| long as the token based auth frontend application exposes
| an url that triggers the action.
|
| ok, now that i think about it: providing an url that
| triggers the (malicious) action is a necessity for session
| based auth but only a possibility in token based auth.
| dwaite wrote:
| XSRF is of the class of confused deputy problems; the
| site expects that the browser will only let the end-user
| that was authorized perform actions, but the reality is
| that the browser on its own has no concept of user choice
| or consent for web content. The hypermedia aspect means
| that content by third parties can by default initiate
| actions, which would override the first-party
| presentation by the site for user consent.
|
| So XSRF protections make it so that third party content
| cannot successfully initiate those actions. Prime
| examples are:
|
| - SameSite cookies, where the initial request from
| another domain will not have cookies presented.
|
| - The local content adding session-related parameters
| unknown to third parties, such as an OAuth access token
| in a header or a session cookie repeated as a form
| parameter.
|
| These protect against the third party being able to
| initiate actions and pick up any background implicit
| authorizations being provided by the browser, such as
| session cookies or HTTP Basic authorization headers.
| jmartrican wrote:
| The token refers to an API token or an OAuth token. These
| tokens are what backend systems require to allow a user to
| access data.
|
| The problem is that these tokens should not be used by your JS
| code (they should never make it to the browser for security
| reasons). So how exactly does a user access their data if the
| front-end code running on their browser does not know the
| token? That's where sessions come in. The webapp hosting the
| front-end JS code (for example Node.js or Java-Spring) creates
| a session whenever a request is made. When the user logs in,
| their token (which the server retrieves though whatever
| authentication method is being used) is stored in the webapp as
| session data. That is to say, for each session, the webapp can
| store information about that session.... e.g. the token. The
| token is then used by the webapp to access the user's data via
| the API calls to the backend. The backend, will then use the
| token to verify the user has access to the requested data (or
| the requested resources).
| slowmovintarget wrote:
| If you have a "true microservices architecture" as the article
| comments, you have a time budget for every request from the
| front end, through the graph of calls on the back-end.
|
| Let's imagine a call-graph budget of 300ms. Every hop after the
| request lands on the back end takes a bite of that. Let's
| imagine a straight stack of calls three deep with no branching.
| Front-end makes a RESTful API call to Service A. Service A
| calls B, B calls C, and C queries a database.
|
| Diving down to the bottom, the database call + networking will
| take 50ms (it's a big query), service C spends 5ms filtering,
| and another 10ms reorganizing the data into its response format
| and returns. 65ms eaten.
|
| Service B deserializes (3ms) and acts on the data (10ms), then
| serializes the response (2ms). 80ms eaten.
|
| Service A gets this back and chews on it for a bit to put it
| into shape for the front end. There's also a layer of proxies
| and caches the request and response goes through, so another 20
| ms here. 100ms! Great! We're under budget (because we're not
| counting network latency back to the UI)!
|
| Now imagine, that for every one of these hops, we add a 10ms
| call to a session service, and processing time (hopefully tiny)
| to review, and error handling. We've also only covered happy
| path, we've not considered call retries, branching calls to
| resolve additional data, each with a +10ms check for that
| session.
|
| Now that time budget is starting to chafe. Validation of signed
| tokens with claims eliminates that 10ms tax on every call, and
| it is a big deal.
|
| How do I know? I work on a system with hundreds of
| microservices. We use JWTs for back-end authentication. Thank
| goodness the article clears us to use JWTs. :)
| BigJono wrote:
| Think you misread, they were asking about the difference
| between a session ID in a cookie, and a bearer token. Not a
| session ID and a JWT.
|
| With a bearer token each microservice still needs to call out
| to an auth service to validate the token, right?
| slowmovintarget wrote:
| Funny thing about the terminology, JWT is just one kind of
| bearer token. The phrase bearer token in the first place
| simply means that if you have the token, assume you are who
| you say you are. (It's also shorthand for passing in the
| authorization HTTP request header with type "Bearer: ")
|
| If you have an opaque token, then yes, you save nothing and
| still require a call to a central service.
| treis wrote:
| One bearer token can map to several session IDs.
| sieabah wrote:
| If you share the hmac key or do private/public key signing
| you're able to distribute the public signing keys to your
| infra.
|
| In doing so you just validate the token against the public
| key. You can then rotate these keys and have a list of them
| to validate against and age off keys which would be the
| last tokens expiration +1 day.
| 0x457 wrote:
| I think it came from misunderstanding a lot of things by people
| making SPAs...
|
| Session cookie is invisible to JavaScript, so handling
| unauthenticated state is a bit weird.
|
| Bearer token is visible to JavaScript, so SPA can make some
| assumption about authentication status. However, it still must
| handle 401s because token could have been revoked or expired
| (JWT solves the expired problem). You also could encode some
| metadata like username and email and instead of having a single
| round-trip to fetch it once, now you will be sending kilobytes
| of cookies with every request.
|
| CORS is a black magic to them. How to properly set cookie is
| also back magic - who would know that you can set cookie in way
| that would include subdomains, so it will be sent to
| www.acme.org AND to api.acme.org.
|
| It was also weird to set a session cookie on outgoing requests
| when service a talks to service b.
| 9dev wrote:
| I think the easiest explanation is this:
|
| Session IDs are bound to cookies from a given origin. Thus,
| every request needs to include the original cookie, and have
| the same origin, to transport authentication data. But cookies
| cannot be accessed from JavaScript, only be sent implicitly if
| these things are true.
|
| This made it difficult to work with separate hosts (www.my.app
| vs. api.my.app).
|
| In contrast, Bearer tokens are explicit: JavaScript code can
| use and attach them to requests as appropriate, thus allowing
| for more control. The also are a little easier to work with (no
| reason to keep a cookie jar, stateless stuff works).
|
| JWTs in turn aren't simply random strings anymore -- they
| contain some claims about the bearer, which are
| cryptographically signed, so whoever receives the token can be
| sure the claims haven't been tampered with. This allows for
| very powerful, interconnected services -- but most people use
| them to pass a session ID around, so... here we are :)
| qabqabaca wrote:
| > This made it difficult to work with separate hosts
| (www.my.app vs. api.my.app).
|
| This is still possible with session cookies if you use
| wildcard origin cookies i.e. *.my.app
| 9dev wrote:
| I know, contrived example. Think of www.google.com and
| googleapis.com. Or CORS issues. There are legitimate
| problems with cross-origin requests.
| 0x457 wrote:
| Yeah, those are issues at google's scale. Almost no one
| who uses them is even at half of google scale.
| GoToRO wrote:
| Session IDs were short, so it was easy to randomly guess them.
| You would get access to some random account.
|
| Also randomly generating them would result in colisions if you
| had a lot of traffic.
| AndrewStephens wrote:
| If your session Ids were short enough to be guessed then you
| were doing it wrong. The simplest thing to do is just use a
| unique GUID as a session id but a sufficiently large random
| number would do just as well.
| bradstewart wrote:
| It's a question of cookies vs HTTP headers. Cookies are hard to
| use with CORS (when using backend-as-a-service stuff like
| Firebase, in particular) and/or mobile apps.
|
| There's really no difference between a random session id and a
| random token. But people typically think "session id == cookie"
| and "token == header".
|
| More secure session cookies are also HTTP-only, which means
| JavaScript code can't access them. Which can be a security
| feature in a lot of cases, but makes SPAs harder.
| ilovetux wrote:
| The way I see it is that session ids are handed out by the
| server as you login and revoked when you log out, so while it
| would be advisable for this session ids to be truly random
| there is no guarantee that this is the case. In truth I have
| seen sessions stored with an autoincrement id meaning that if
| you were to get a session id you could, in theory, just use the
| number before yours and highjack someone else's session.
|
| A bearer token, in my experience, is a cryptographically
| generated random key (presumably unguessable) which the user
| can create, use and revoke at will.
| stefs wrote:
| if someone is dumb enough to use guessable session ids
| (instead of, e.g. uuids), then they'd be also dumb enough to
| use guessable bearer tokens.
| tyingq wrote:
| I think they are trying to point out that a "session ID" is
| typically a key into session data stored on the server side,
| where the token is usually not just a key, but all the session
| data stored client side. With signing and other mitigations
| intended to keep that from being dangerous.
|
| >Why were sessions inadequate
|
| I suspect there are more reasons, but one is likely CORS and
| the tendency for the auth infra to be separate from the app
| infra.
| WA wrote:
| Session ID: Server has a list of valid sessions. Compares your
| session ID to its own list. Have session token? Access granted.
|
| JWT: Server doesn't keep a list. Someone comes with a JWT,
| which is signed by the server. Server grants access purely on
| the fact that there is a signed token.
|
| JWTs are useful if you don't want to sync the server-side list
| of valid session ids (many microservices for example).
| 0x457 wrote:
| That's not entirely true. Session cookie doesn't have to be
| opaque and can be signed and encrypted. That's how RoR been
| using it for long time. Realistically, you only need to make
| sure that an unexpired and verified session hasn't been
| revoked.
|
| Opaque sessions are smaller, though.
| [deleted]
| sascha_sl wrote:
| >Issue solved, right? Well, kind of, except you've just
| reintroduced a bearer token, because that's exactly what the
| refresh token is.
|
| Entirely missed the point that refresh tokens will not work when
| your app (or your user) has told the IdP to sign out - unless you
| requested offline access.
| eagsalazar2 wrote:
| Hallelujah! I've been saying this for years and people looked at
| me like I was insane. JWTs are one of the biggest cult/hipster
| scams of the last few years.
|
| GraphQL everywhere is another one (like JWTs, it has its uses but
| people went way overboard) and Tailwind is the next one.
| xtracto wrote:
| I see a lot of the comments in this thread from people that
| apparently just do not get the problem with JWTs. I was using
| "presigned access/bearer tokens" way before JWT was a thing.
|
| There's multiple safe and easy ways to do
| authentication/authorization without providing a signed green
| card to the client which you don't have the ability to cancel
| at any time. As long as you keep control in a database.
| hbrn wrote:
| Microservices and Serverless are another two.
|
| IMO the only hyped up piece of tech that was actually good in
| the last decade is containers (but not the orchestration, just
| containers).
| eagsalazar2 wrote:
| Serverless? Huh I can't get onboard with that one.
| Sohcahtoa82 wrote:
| Serverless is great for scenarios where you don't want to
| have to worry about scaling.
|
| AWS S3 was probably the first "serverless" technology to
| really take off. You don't have to worry about how many
| hosts you need to adequately handle sharing a file with
| millions of people. You don't need to start up multiple
| hosts in order to get backups in multiple data centers and
| then handle synchronization. Nope, just put a file there,
| and everything else is handled.
|
| AWS Lambda and other FaaS providers made it so you don't
| have to configure scaling hosts to handle sudden large
| loads. It's also great for things that only need a couple
| minutes of CPU time per month, like say, periodically
| processing some data in batches. You don't have to deal
| with standing up a host that runs a cron job and then
| paying for CPU time you're not using.
|
| Serverless databases mean you don't have to worry about
| patching the underlying operating system.
|
| Of course, serverless has its limitations. FaaS is not
| suitable for long-running tasks, for example. It also tends
| to result in vendor lock-in.
|
| EDIT: If you're going to fire back with "AlL tHoSe ThInGs
| ArE sTiLl RuNnInG oN sErVeRs", don't waste your time. It's
| a tired argument that misses the point. Yes, they run on
| servers, but you don't have to care about the underlying
| servers.
| hbrn wrote:
| Infinitely scaling webservers is trivial. Serverless
| solves a problem that is already easy to solve.
|
| Infinitely scaling database means inevitable tradeoffs.
| Serverless doesn't take those tradeoffs away.
|
| It doesn't do anything to justify the hype.
|
| You get complexity, worse DX, vendor lock-in and often
| higher price for the sake of not having to do security
| updates. This is the worst deal in the history of deals,
| maybe ever.
| eagsalazar2 wrote:
| Your entire last sentence, except the higher price bit,
| is just completely utterly false on its face. Serverless
| is magically simple - and that _is_ DX, magically simple
| and easy to reason about. Vendor lockin?? Wat? Again
| utterly wrong and false. The whole point is you are
| running your code, you define the function bodies, they
| just get deployed to different serverless hosting
| services and it is insanely simplifying. Switching from
| AWS to Netlify to Vercel is not harder than switching
| under any other approach.
|
| About cost, even though that isn't "completely utterly
| false" it is misleading. If you are a startup and want to
| move as fast as possible without incurring tons of tech
| debt, and don't yet have millions of daily users, you
| should use serverless functions because the total cost
| will be relatively small (relative to the insanely high
| cost of good devs that is -- always optimize for DX and
| velocity until you actually have scale). If you ever get
| to the point where serverless is costing you real $$,
| then refactor then - you've incurred almost no tech debt
| by waiting and being very very fast prior to that may
| just be the reason you got to that scale at all.
|
| Go ahead and build a serverless app utilizing a framework
| that is well aligned with that approach (may I recommend
| Remix?) and I bet you'll change your tune. My gut tells
| me you have no firsthand experience because your points
| are absolutely off base.
| elforce002 wrote:
| What's wrong with tailwind? It made css styling accesible and
| easy at least for me.
| Ocha wrote:
| I think this article is shortsighted: what about not needing to
| store token at all? I think that is one of the biggest strength
| to it - you can issue short lived tokens in high volume without
| needing to store anything anywhere.
| bjt2n3904 wrote:
| I was significantly creeped out that my state was issuing COVID
| passports with JWT.
|
| Especially when I signed the opt out form requesting to not be
| placed on the database.
|
| Especially 2.0, when I called the database, and was told that my
| records could not be deleted.
| MAGZine wrote:
| your vaccination record does not need to be invalidated, so
| there is no issue.
|
| it's a standard format for transmitting data signed by an
| entity.
| misnome wrote:
| How is issuing a JWT implicitly mean that they are storing it
| in a database? Couldn't they have just... not stored the
| data... but can still validate it?
| infamousjoeg wrote:
| Exactly this. They just need to validate the JWT signature
| against a JSON Web Key Set (JWKS). There's no need to store
| the data.
| tylergetsay wrote:
| They used "JWTs" as a convience, Smart Health Cards have none
| of the issues here. Their issuance doesnt require a database
| either, thats just your healthcare provider.
| adolph wrote:
| JWT and SMART Health Cards are similar under the hood. The T
| part of JWT refers to a Token, which in JWT is a standardized
| set of claims. Instead of T, SMART Health Cards use S, or
| Serialization. Instead of a token, the JWS serializes FHIR
| demographic and necessary medical information about the card
| bearer. SHC has all the same invalidation and key
| issue/revoke challenges as JWT.
|
| _When the issuer is ready to generate a Health Card, the
| issuer creates a FHIR payload and packs it into a
| corresponding Health Card VC (or Health Card Set)._
|
| _The VC structure (scaffold) is shown in the following
| example. The Health Cards framework serializes VCs using the
| compact JWS serialization, where the payload is a compressed
| set of JWT claims (see Appendix 3 of RFC7515 for an example
| using ECDSA P-256 SHA-256, as required by this
| specification). Specific encoding choices ensure
| compatibility with standard JWT claims, as described
| athttps://www.w3.org/TR/vc-data-model/#jwt-encoding._
|
| https://spec.smarthealth.cards/
| eagsalazar2 wrote:
| This is actually a decent use of JWTs (and generally I'm in the
| JWT=='bad' camp). These things don't ever need to be revoked
| and issuing a JWT means they don't have to store it in their
| own database. Also 3rd parties checking that passport don't
| have to implement any kind of database lookup/integration with
| state APIs. Pretty solid architectural choice IMO.
| bjt2n3904 wrote:
| Oh yeah, exactly! Perfect application. I hate it.
|
| Makes it really easy to scatter low budget devices across the
| state that require no Internet connection, and gives a red
| light/green light on whether you're allowed to go shopping or
| be out in public that day.
|
| All the infrastructure is right there in place. Didn't get
| that far.
|
| This time.
| eagsalazar2 wrote:
| Businesses have the right to refuse service for any reason
| that is non-discriminatory. That's a good thing even if you
| don't agree with their reasons. I know some people do, but
| I don't count "you can't come in here if you can't prove
| you aren't going to kill me by negligently infecting me and
| my customers with covid" as discriminatory. Anyway covid is
| over so let's all move on.
| marcosdumay wrote:
| Most of the "infrastructure" is the store owners and etc
| complying with the passport system. The technical part is
| irrelevant.
|
| Anyway, all of that "infrastructure" is people making
| judgments. It will enable a police state if the people
| think they should enable it.
| eagsalazar2 wrote:
| And you don't need all this tech for a police state
| anyway. Police states have been around for a long long
| time. Doomsday worries about hypothetical uses of tech
| seem yeah very hypothetical. I agree we should fight govt
| encroachment on our rights, but if you do that with too
| paranoid of a lens, then we won't have nice things (USA
| 2022 in a nutshell). How about, let's fight govt
| oppression when/where they are actually oppressing us.
| quesera wrote:
| Exactly. JWTs assert _claims_.
|
| If your claim is not durable enough for the protocol's
| expiration setting (e.g. authorization, in some cases), then
| don't abuse JWTs. Simple!
| eagsalazar2 wrote:
| This is really nice way of putting this. Logging in/out is
| _authorization_ (and authn) so generally if you want to
| revoke access (log people out), JWTs are possibly the wrong
| choice (the thing people use JWTs for 99% of the time)
| xorcist wrote:
| > These things don't ever need to be revoked
|
| What would the procedure that can guarantee no mistaken
| passport will ever get signed look like?
|
| Of course you need some form of revocation, otherwise the
| first bad apple will spoil the system.
| stunk wrote:
| I was significantly creeped out that my state was issuing COVID
| passports.
| bhandziuk wrote:
| Are you aware governments have been tracking vaccines and
| issuing vaccine cards for long long before COVID happened?
| oautholaf wrote:
| In my experience, a working strategy for handling signout or
| revocation for statically verifyable tokens like JWT is
| straightforward:
|
| - Clear client side state where you can. - Write signed
| out/expired tokens to something with a cheap heavy read/eventual
| consistency model - Fail to signed in if unavailable -
| Acknowledge that you are gaining latency/availability/ lower
| costs by trading some precision
|
| I am aware of a very large website most folks use every day that
| did this for more than a decade and it worked fine.
| jdthedisciple wrote:
| great idea! I'm using JWT in one of my projects and still
| unsure how to fix the irrevocability of JWT while keeping them
| stateless. But this seems like a nice intermediate solution
| nfw2 wrote:
| Not super-opinionated on this, but let me try to steelman the JWT
| case.
|
| Yes, to perform session invalidation with JWT requires some
| amount of centralized state, so a perfectly-decentralized auth
| system is not actually possible.
|
| However, using JWT means this centralized state is much less
| heavy than traditional sessions. Instead of saving ALL sessions
| and ALL session data on the server, JWT only requires saving a
| small fraction of session ids (those that have been recently
| invalidated) and NO session data. By using JWT, you have
| simplified your memory needs and reduced them by multiple orders
| of magnitude.
| Salgat wrote:
| The only argument they elaborate on is still easily addressed
| with a validation of the token against a database on every
| request, and it's hardly a drawback compared to other methods if
| the other methods do the same thing.
| fknorangesite wrote:
| Well, it's a drawback because if you're doing the validation
| anyway, why bother with the added complexity of the JWT in the
| first place? You've removed the primary advantage.
| Salgat wrote:
| I'm not sure why JWTs are considered complex. In C# I
| generate tokens with a couple lines of code. Sure you can get
| fancy with the claims, but it's not required if you're going
| to access the DB anyways on authenticated requests.
| fknorangesite wrote:
| You're right, they aren't, particularly. Just moreso than
| alternatives - especially if
|
| > it's not required if you're going to access the DB
| anyways on authenticated requests.
|
| then what's the point? You're just re-inventing bearer
| tokens.
| Salgat wrote:
| I can see one massive advantage is that you could disable
| the db access/verification for service-to-service
| communication, since revoking an internal service token
| is exceptionally rare (and if it occurs, you have much
| bigger problems to worry about).
| fknorangesite wrote:
| Which is exactly the kind of valid use case already
| presented in OP.
| gumby wrote:
| Thankfully, recent events have changed the expansion of JWT in my
| brain's cache: when I first read this I thought "James Webb
| Telescope".
|
| Not being a fan of JWT authentication I was doubly relieved when
| I clicked through.
| 0x457 wrote:
| I think the only two ways to use JWT tokens is:
|
| 1) you have some kind of api gateway, it would validate the
| session, issue JWT/PASETO that lives a minute or so, downstream
| services would use that token to talk to complete the request.
|
| 2) When you need something like AWS S3 presigned URL.
|
| Everything else is just an opaque session token with extra steps.
| wdb wrote:
| Cookies are quite problematic these days
| red_trumpet wrote:
| Care to elaborate on that?
| eagsalazar2 wrote:
| Probably means "socially problematic" because there is real
| orthodoxy here. If you speak badly JWTs in an interview...
| that's probably problematic.
| wdb wrote:
| If people have cookies disabled, or when you try to load
| content in a semi-sandboxed manner by using IFRAMEs than you
| can't use cookies, localStorage, sessionStorage etc.
| marcosdumay wrote:
| You mean first-party session cookies disabled?
|
| How many people do that? Or better, how many people do that
| and still expect anything to work? (Anyway, iframes don't
| break those in any way.)
| weinzierl wrote:
| Apart from the issue of not being able to log out there is the
| following list in the article:
|
| _" We've ignored things like increased attack surface due to
| complexity of JSON parsing/validation, misconfiguring JWT
| libraries to allow no signature, leaking data to users because
| you thought JWTs were encrypted (they're usually not by default),
| and poor implementation of API clients that don't properly re-run
| requests that failed due to expired JWTs. The list goes on, and
| on, and on ..."_
|
| All of these sound like usability issues. Not to downplay them
| but are there more serious issues with JWTs?
| pionar wrote:
| It's like C. The language is fine, it just allows you to do bad
| things too easily. The same is true with JWTs. The idea is
| fine, but allows you to do bad things too easily.
|
| The no signature bit was a huge flaw in almost all JWT
| libraries across languages a few years ago.
|
| There are better schemes out there.
___________________________________________________________________
(page generated 2022-09-29 23:03 UTC)