Hacker News new | past | comments | ask | show | jobs | submit login

I don't care if you want to use stateless client tokens. They're fine. You should understand the operational limitations (they may keep you up late on a Friday scrambling to deploy a token blacklist), but, we're all adults here, and you can make your own decisions about that.

The issue with JWT in particular is that it doesn't bring anything to the table, but comes with a whole lot of terrifying complexity. Worse, you as a developer won't see that complexity: JWT looks like a simple token with a magic cryptographically-protected bag-of-attributes interface. The problems are all behind the scenes.

For most applications, the technical problems JWT solves are not especially complicated. Parseable bearer tokens are something Rails has been able to generate for close to a decade using ActiveSupport::MessageEncryptor. AS::ME is substantially safer than JWT, but people are swapping it out of applications in favor of JWT.

Someone needs to write the blog post about how to provide bag-of-attributes secure bearer tokens in all the major programming environments. Someone else needs to get to work standardizing one of those formats as an alternative to JWT so that there's a simple answer to "if not JWT then what?" that rebuts the (I think sort of silly) presumption that whatever an app uses needs to be RFC standardized.

But there's a reason crypto people hate the JWT/JOSE/JWE standards. You should avoid them. They're in the news again because someone noticed that one of the public key constructions (ECDHE-ES) is terribly insecure. I think it's literally the case that no cryptographer bothered to point this out before because they all assumed people knew JWT was a tire fire.

> they may keep you up late on a Friday scrambling to deploy a token blacklist

Because every token has an iat datetime, you don't need a token blacklist to invalidate tokens. You just need some sort of tokens_invalid_if_issued_before_datetime setting that gets checked whenever you validate the signature of a token.

The alternative is to store a UUID for each user, and just rotate those whenever they log out, change or reset their password, or there is some sort of security event. These are then stored in the payload and used as a secret. The one advantage over just using dates is that with the former, there can be weird bugs if you have multiple servers with clocks that are out of sync.

But you shouldn't ever need to blacklist specific tokens, at least not unless you have some highly specialized use case.

Agreed. Revoking all user sessions instead of a specific token is the common case. The only usage I see for revoking a specific token is when the user is deactivating a specific client.

Also when a user changes his password, no ?

> The alternative is to store a UUID for each user

Is that not effectively a server-side session?

> Is that not effectively a server-side session?

With most web frameworks (e.g. Django), the user model is retrieved on every request anyway. So it would be perhaps more accurate to say that it's a server-side session that's effectively not a server-side session, since no additional lookups are needed, only the user model lookup that's already done anyway.

So every user on your system has to reauthenticate if one client token is compromised? That seems like an invitation to a thundering herd. Not necessarily fatal, but I'd consider it a nice feature to not have to invalidate everybody's tokens to get at one.

> So every user on your system has to reauthenticate if one client token is compromised?

No, because you would also store either a separate datetime or uuid on each user model. And if just one user has their credentials compromised, then you would bump the date or generate a new UUID for just that user.

The global datetime would only be bumped if some site wide vulnerability were found.

So there is a db roundtrip involved? Like a inverted session. Whats the point of using jwt then?

For most web frameworks, the user model gets retrieved from the db automatically whenever an authenticated request is made. So there is no extra lookup.

Oh man.

Proponents say critics dont offer alternatives at the same time they always literally 'reverse' engineered sessions if you dig deep enough.

I give up. JWT is just a hip thing to do right now. :(

Got it, didn't catch that you were referring to storing that timestamp per-user.

That's what I do in my system.

Assuming that:

- your JWT libraries don't do anything dumb like accepting the `none` algorithm

- you're using HMAC SHA-256

- your access tokens have a short (~20 min) expiration time

- your refresh tokens are easily revocable

Can you elaborate on the specific security advantages that a token encoded with ActiveSupport::MessageEncryptor would have over such a JWT implementation?

Why do you think there aren't more AS::ME implementations out there if it's a superior solution? I only know of a Go implementation and haven't seen others: https://godoc.org/github.com/mattetti/goRailsYourself/crypto


I saw you mention Fernet in another comment. As a Heroku alum I'm quite familiar with Fernet (we used it for lots of things), but to my knowledge those projects are on life support at best.

You should also make sure to allow only tokens with the "HS256" alg headers before you verify them, in case somebody decides to add a new signature algorithm to your library, and it turns out it could easily be broken and lets you use the same key you used for HS256.

If it's your software generating the tokens, then that means they'd need the shared key, or private key in order to sign the token... which is already a problem. Now if you're accepting tokens from a third party, that's another issue, and should be much more constrained.

I go farther still and require a VERY short expiration on service to service requests (documented as 1m, coded as 2m) which combined with https limits the chance of replay attacks.

yes, that's another good point and probably something many folks mess up. I am explicitly specifying my algorithm for both encode/decode :)

> using HMAC SHA-256

HMAC is great for monolithic architecture, but I've quite enjoyed using asymmetric RS256. I don't think that's something AS::ME offers.

I'd also be interested in hearing an answer to this from tptacek.

My (limited) understanding is the security issues arise around the implementation & handling some of the default claims (NBF, IAT, etc.) and producing/verifying the signature.

But I don't quite understand how moving to a different format solves these issues?

I appreciate that this comment is wise from a cryptosystems perspective, i.e. there are a number of ways to do JWT wrong, not enough safety guards, etc., but is there not a subset of JWT that is safe to use?

The OP article makes it sound like it's impossible to use JWT correctly, but I was under the impression that if I 1) am the issuer, and 2) I hardcode a single algorithm on my API endpoints, that neither of the issues in the OP apply. (The EC issue would apply if that algorithm was chosen).

Is there a safe subset of JWT? And isn't there value to small players in using the safe subset of JWT which is battle-hardened by guys with big security teams like Google?

It's not that need an RFC standardized solution for everything, but I'd rather not roll my own anything related to crypto. Would something like crypto_auth(json(bag)) be better here? (crypto_auth from libsodium, json being sorted without whitespace)

Yes, that would be much better, and it's what I mean when I say that JWT doesn't bring anything to the table.

"json being sorted without whitespace"

What is the significance of that part?

It makes JSON deterministic, which it isn't by default (e.g. {"foo": 1, "bar": 2} and {"bar":2,"foo":1} are both valid serialisations.

Of course, it'd be better still to use a format _meant_ to provide human-readable canonical representations of data, e.g. Ron Rivest's canonical S-expressions (http://people.csail.mit.edu/rivest/Sexp.txt), but of course this is information technology and we have to reinvent the wheel — usually as an irregular polygon — every 3-4 years rather than using techniques which are tried and true.

Ah yes, similar to canonicalization of XML for XMLSignature?

Presumably this means that you have to have have a "flat" JSON structure rather than lots of nested objects and arrays?

Afaik you just need to alphabetize the properties of every object

> there's a reason crypto people hate the JWT/JOSE/JWE standards. You should avoid them

Could you give more info about this? If ECDHE-ES is avoided why else is JWT insecure?

Seems like, practically, that suggests three options:

1. Take something like AS::ME that already has real use and implement it for as many platforms as possible

2. Define a really restricted subset of JWT (which may be necessary anyway for purposes of saying to management "yes, we're buzzword compliant")

3. Invent a non-AS::ME "bag-of-attributes secure bearer token" system and implement it everywhere.

I think part of the trouble with 3 is that people like me genuinely worry that if we tried to roll our own we'd manage to do worse than JWT in spite of JWT being terrible.

So maybe step zero is for somebody with crypto knowledge to explain one sane way to do the "bag-of-attributes secure bearer token" part ... or you to point the audience to a blog post that already exists that describes it, because, well, because I suspect quite a few of us trust you to say "this post actually describes a sensible plan" while we don't trust ourselves to be able to tell.

Or just use Fernet:


Fernet was written originally for Python but there's a Ruby implementation, a Golang implementation, and a Clojure implementation. I believe that for at least 80% of applications considering JWT, Fernet provides exactly the right amount of functionality, and does so far more safely than JWT.

Since it's not linked from any https://github.com/fernet/ project as far as I could tell, and I had to google for it.

Clojure implementation:


I also found a JavaScript implementation:


This looks very much like the approach to session-data-in-encrypted-and-signed-cookie I've seen used to great success in lots of places (where for a stateless-ish API the contents are just a user id or whatever).

Am I right that this would work fine both in that or in e.g. a query parameter?

(sorry if I'm asking really stupid questions, but I'd rather look stupid than accidentally a security hole)

1. The reason AS::ME can be that nice is because it assumes a monolithic architecture and a single framework.

For example, AS::ME relies on shared secrets, which I think makes it unfit for distributed systems. Implementing JWK with asymmetric keys can really reduce provisioning and configuration costs. Keeping the signing secret on one private, hardened auth server (or cluster) also allows smart things like automated key rotation.

2. 100%. There's at least one right way to do JWT, but more ways to do JWT wrong.

3. JWT et al provide a fine starting point, I don't see a reason to start from scratch.

I'm not tied to the JWT spec, but I'm quite happy with what I've been able to accomplish using a careful implementation in my AuthN server: https://github.com/keratin/authn

Agreed... my first two experiences with JWT were creating my own implementation... in my case, the allowed public keys had to come via https from a specific server in the domain, even without PKI using shared key... I had hard coded the algorithm used for the signature. This could just as easily be filters on a library though, it's just my first experience didn't have a valid library, so I had to composite one (did use existing crypto library though).

JWT is a perfectly valid structure, even if the spec is more flexible than it should be. By that matter, https also has historically supported algorithms and protocols later broken. Nobody is suggesting we stop use HTTPS, only that we limit acceptable protocol and algorithms supported.

No, almost everybody in the field laments SSL and TLS. It's probably too late at this point --- and has been for well over a decade --- to get to something better than TLS, and so TLS 1.3 is what we're stuck with. But that is demonstrably not the case with JWT. We don't have to convince all the browser vendor to upgrade out of JWT in lockstep. Avoiding another 20 years of hair-on-fire crypto vulnerabilities seems reason enough to lobby against that spec.

But any given algorithm today may not be sufficient tomorrow... so we just don't use ANY encryption? JWT is a perfectly valid structure.. there are options as to signing, so use/limit as needed.

And I think JWT is more flawed than SSL/TLS.

For my service to service requests, I tend to require the token itself be set to an expiration of less than 1 minute from creation. I actually code 2min in the check, but document 1 for access clients. This allows for more than enough drift and with https mitigates the level of risk for replay attacks.

Beyond this a header/signature for the body/payload will reduce the risk of the rest.

As to being able to select the signature algorithm, or set the uri for the public key... ignore this, or whitelist domains or methods. Yes, there's some wholes regarding a "by the books" implementation... that doesn't mean you need to support the entire spec.

I implemented about 1/2 the SCORM spec in an API once, and it was 8 years before a specific course needed a part that was missing. Yes, it isn't 100% compliant, but if it does the job, and is more secure as a result, then I'm in favor of it.

It seems that Matthew Green warned them about some of their choices though back in 2012!! https://www.ietf.org/mail-archive/web/jose/current/msg00366....

>that rebuts the (I think sort of silly) presumption that whatever an app uses needs to be RFC standardized.

I thought crypto mantra was "Never roll your own." An RFC (Request For Comments) is a literal attempt to follow that advice by seeking the advice of cryptographers who are presumably smarter at coming up with crypto standards. Where were the cryptographers during the draft phase when comments were being solicited?

>I think it's literally the case that no cryptographer bothered to point this out before because they all assumed people knew JWT was a tire fire.

Oh. Cunningham's Law. You know, if you're not part of the solution, you're part of the precipitate.

That's some considerable JWT fallout, since companies making a business out of security are endorsing it. Auth0 for example, https://auth0.com/docs/jwt

Until I read this article, I was under the impression JWT was the best new thing.

I don't care about these moral arguments. I'm making a simple, positive claim: JWT is bad. You can blame whoever you'd like for it being bad, but as engineers, you need to understand first and foremost that JWT is bad, and reckon with your feelings about that later.

You have a responsibility to built trustworthy systems, and you get no pass on building with flawed components simply because you wish experts had made those components less flawed.

"Contribute to standards processes" and "don't roll your own" aren't moral arguments. They're complimentary pieces of practical advice on how to make trustworthy systems.

Meanwhile your comments bury whatever substantive content they might hold under layers of emotional, accusatory garbage. Maybe get those feelings locked down a bit before posting?

Do you have any recommendations for SPAs where the API is hosted on a different subdomain than www? I think everyone agrees that JWT is a bad spec, the problem is that setting cookies across subdomains ranges from difficult to impossible.

If you have access to an experienced devops team who can securely maintain an nginx server with some proxy logic then maybe that's a possibility, but otherwise what other viable options are there? Wishing that JWT were more secure won't make it so, but neither will wishing that CORS were more flexible. And if it's a choice between subclassing the JWT handlers to provide a couple extra security checks vs trying to securely configure and maintain a whole extra proxy setup, then the former seems like the lesser of the evils.

Does this mean that using AWS Cognito [1] is out of the question since it uses JWT? Unfortunately, you can't change what the service uses as it's all under Amazon's control.

[1]: https://docs.aws.amazon.com/cognito/latest/developerguide/am...

I have no feelings on the subject. I don't use JWT. I just want to point out this sounds like (and continues to sound like) "Roll your own" advice to me.

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