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.
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.
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.
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.
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. :(
That's what I do in my system.
- 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.
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.
HMAC is great for monolithic architecture, but I've quite enjoyed using asymmetric RS256. I don't think that's something AS::ME offers.
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?
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?
What is the significance of that part?
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.
Presumably this means that you have to have have a "flat" JSON structure rather than lots of nested objects and arrays?
Could you give more info about this? If ECDHE-ES is avoided why else is JWT insecure?
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.
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.
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)
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
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.
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.
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.
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.
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?
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.