[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)