So, as someone who does some work in crypto engineering, arguments about JWT being problematic only if implementations are "bungled" or developers are "incompetent" are sort of an obvious "tell" that the people behind those arguments aren't really crypto people. In crypto, this debate is over.
I know a lot of crypto people who do not like JWT. I don't know one who does. Here are some general JWT concerns:
* It's kitchen-sink complicated and designed without a single clear use case. The track record of cryptosystems with this property is very poor. Resilient cryptosystems tend to be simple and optimized for a specific use case.
* It's designed by a committee and, as far as anyone I know can tell, that committee doesn't include any serious cryptographers. I joked about this on Twitter after the last JWT disaster, saying that JWT's support for static-ephemeral P-curve ECDH was the cryptographic engineering equivalent of a "kick me" sign on the standard. You could look at JWT, see that it supported both RSA and P-curve ECDH, and immediately conclude that crypto experts hadn't had a guiding hand in the standard.
* Flaws in crypto protocols aren't exclusive to, but tend to occur mostly in, the joinery of the protocol. So crypto protocol designers are moving away from algorithm and "cipher suite" negotiation towards other mechanisms. Trevor Perrin's Noise framework is a great example: rather than negotiating, it defines a family of protocols and applications can adopt one or the other without committing themselves to supporting different ones dynamically. Not only does JWT do a form of negotiation, but it actually allows implementations to negotiate NO cryptography. That's a disqualifying own-goal.
* JWT's defaults are incoherent. For instance: non-replayability, one of the most basic questions to answer about a cryptographic token, is optional. Someone downthread made a weird comparison between JWT and Nacl (weird because Nacl is a library of primitives, not a protocol) based on forward-security. But for a token, replayability is a much more urgent concern.
* The protocol mixes metadata and application data in two different bag-of-attributes structures and generally does its best to maximize all the concerns you'd have doing cryptography with a format as malleable as JSON. Seemingly the only reason it does that is because it's "layered" on JOSE, leaving the impression that making a pretty lego diagram is more important to its designers than coming up with a simple, secure standard.
* It's 2017 and the standard still includes X.509, via JWK, which also includes indirected key lookups.
* The standard supports, and some implementations even default to, compressed plaintext. It feels like 2012 never happened for this project.
For almost every use I've seen in the real world, JWT is drastic overkill; often it's just an gussied-up means of expressing a trivial bearer token, the kind that could be expressed securely with virtually no risk of implementation flaws simply by hexifying 20 bytes of urandom. For the rare instances that actually benefit from public key cryptography, JWT makes a hard task even harder. I don't believe anyone is ever better off using JWT. Avoid it.
This should probably be the first thing anyone thinking of using JWT reads.
This is certainly true for schemes that require trips to a source-of-truth database to authorize a token (c->auth, c->resource, resource->auth). It's also true for schemes where the token is associated with capabilities that are loaded from a database. Using JWT to implement RBAC is flawed.
However, there is a strong use case for the token carrying its own capabilities -- that is, a token that is more than just "20 bytes of urandom".
If a resource service can derive the capabilities associated with a token generated by a trusted authentication service without contacting that service, that has real world implications for lower latency, higher throughput applications that are simpler to compose and operate.
As far the cryptographic credibility, the idea behind capability-based security is an old one, and I'm sure you're aware of the research. This particular spec may be problematic, folks may be misunderstanding and misusing the primitives, but the underlying idea is sound.
Previous HN discussion of CapSec Wikipedia article
The issue isn't with capabilities, or delegated authentication, or public key tokens, or even standardizing any of those things. I think at this point I've been pretty clear about what I believe the issues actually are.
Do you have a standard that you would recommend for any of those things?
- crypto_auth, or HMAC-SHA256 by itself, for authentication
- crypto_secretbox for symmetric encryption
- crypto_box or TLS for public key encryption
Echoing tptacek's comment above: the problems with using those individual pieces is in the joinery - combining them in ways that are broken.
Can you point us to an article showing how to implement this for a web app communicating with an API? Lacking crypto expertise and documentation, the average programmer is going to use something like Auth0, and if his users' recipes or jogging history is compromised, so be it.
I recently set up libsodium for a client running Node.js on the server, and could work on this for you as well, if you want to send me an email, I can send you my rates.
My value proposition is I've shipped a lot of useful things for companies, and found security vulnerabilities, and those skills are in demand these days, I guess.
You can read more here: https://burke.services
But you need alternatives.
NaCl is not alternative, because you need to base a protocol on top of that. Noise is not an alternative because it's not meant for the same purposes. Fernet is the only thing close to be an alternative, but it lacks useful features (for instance, how do you specify a key ID for key rotation), supports only symmetric encryption, has a weird cipher choice, and barely gets any library support.
Regardless, bad engineering is bad engineering. Bad security engineering gets people hurt. It's not that JWT doesn't do the best job it could do: it's that it's a snakepit of implementation traps that create vulnerabilities. For me, the argument ends there.