If you're like me and are wondering what Macaroons are, some searching revealed to me that this is the 2014 paper [1] that introduced them to the public. It's a nested, chained HMAC construction that's useful for delegation, and here's a library and some code examples [2] that one can play with to get a feel for what they do and how.
No wonder it's not well known: it hasn't been picked up by the blog treadmill where dudes on Medium post half-baked info they just found out about, and isn't being pushed by commercial auth proxies.
On that note, posts by Latacora or affiliated persons, there and here, seem to mix well-researched opinions and advice with in-jokes that are lost on all but other experts, assumptions of an inconsistent amount of domain expertise, and quips that muddy some topics more than a bystander would reasonably expect. Why not be more dry and less wry, include links, and morph the FUD around JWT to something real?
I'm fucking around, but really the answer is: if we didn't have the presumptive informality of a "blog" or some-such, we just wouldn't write; we'd get 20% of the way through a draft and just pick at it, hoping to make it more correct and authoritative, until our will to keep going evaporated. I have a whole folder full of things I started doing that with.
The in-jokes and snark are what trick us into writing in the first place. There's no getting rid of it.
I'm hopeful that people can at least appreciate that we aren't confining this stuff to Twitter threads anymore.
Re: JWT; sure, but it’s a multifaceted problem that’s more than one blog post. We’ll get there. Unfortunately, we’ve been saying most of that for years now, just because it’s not easily accessible in one place (a valid criticism!) doesn’t mean I’m not exhausted talking about it :)
How would a service come into possession of a Macaroon good for another service? I could see getting a macaroon for a username/password/2FA in a frontend context, but how does it solve backend service authentication?
Is this finally the more expansive statement from tptacek than just "Don't use JWTs" that I've been waiting for?
Still feels like we're waiting for another shoe to drop in this space - maybe it really is Macaroons?
But since container-driven microservice orchestration is ultimately destined to recapitulate the whole of CORBA and DCOM and therefore probably kerberos and every flavor of PKI ever attempted before it gets blown up and replaced with something leaner and simpler and based on shared secrets again, I don't hold out much hope.
It is partially. The problem with the question of what you should use instead of JWT is that it presupposes a usually-wrong assumption that you actually want something of the same shape as JWT, which is usually not true. JWT is a bad answer to the wrong problem: addressing the bad answer part doesn’t address the wrong problem part.
To riff off of jackhammer questions[*], just because chainsaws like PASETO exist and a chainsaw is more effective than a jackhammer at a specific task, doesn’t mean you really wanted a chainsaw.
So yes, this is precisely the problem with sending the message 'JWT is bad' when what you really want to send is 'bearer tokens are bad (and JWT is a badly designed bearer token)'.
I am reminded of the situation a few years ago where the message 'stop hashing/salting passwords with SHA1' got widely interpreted as 'Okay, I'll salt and hash with SHA256', when the real message that was needed was to use bcrypt.
That argument doesn't work, because JWT isn't just a "bearer token". It's also potentially an asymmetric token, or any other mechanism someone tries to shoehorn into it next week. That's one of the problems with metaformats.
This post isn't a comprehensive argument against JWTs and isn't intended to be. We can't have the conversation about JWT in earnest until we understand the problem domain.
I'm not really looking for a comprehensive argument against JWTs (they're a terrible solution to the wrong problem is a reasonable enough argument); I am looking for a path to move the conversation forward from 'Well OAuth2 supports JWTs as a way to authenticate without making a callback to the auth server, and we know we shouldn't try to roll our own scheme so HMAC(timestamp) seems a bit hacky, and macaroons seem halfbaked, and cert management gives me nightmares, so isn't just using what OAuth2 provides the safest option? After all, everybody else is doing it...'
Everybody is doing it (at least where I'm from). There's good framework/vendor support for OAuth 2 and JWT breaks the nexus between the services and the magic auth server that must be called on each and every request.
Even with the horrors of the implementation ({"alg": "none"}) It's a risk that many organisations are willing to take.
Right, but it sounds (if I'm understanding you right) like you're talking about JWT-and-things-as-a-way-for-your-services-to-talk-to-each-other-through-the-client. This is roughly analogous to Ruby cookies - save yourself a database read through the power of maths.
But tptacek seems to be saying 'I want you to understand all this S2S stuff before I can begin to rant at you about JWT'. That's a bit different and I'd like to get the rest of this newsletter.
No, I meant in the context of server to server comms, as well as end user authn/authz.
I would say that jwt is becoming the standard for s2s. I’ve personally worked with a dozen or so corporate APIs that use it and basically all the Microsoft Azure / 365 services are secured with jwt.
I’m intrigued by macaroon because as tptacek points out JWT is easy to get wrong, you’ve got to deserialize json before you can authenticate the request and it’s also tempting to stuff all manner of things into the token claims.
FWIW, this matches my experience. Large new enterprise systems have JWT all over the place.
My first pass counterargument to this is: great! You also have FAANG’s security budget and know how to find and resolve bugs like the FB OAuth2.0 tokens being replayable from 1 relying party to another, right? No? Oh.
The general subtext being: that’s nice but you know nothing of their rationale, underlying work that went into securing it, etc; so if you’re picking up a token metaformat without the massive work behind it, you’re just cargo culting.
Yep, that makes sense. I'm having a bit of a 'oh, so that's why' moment while also feeling a little envious of a subfield where 'the inadequate size of your budget' is a viable starting point for a constructive conversation with a client.
That's an argument from/with random people on the Internet, not clients. The nice thing about being around clients for a long time is that you build a working relationship with them based on mutual trust and respect.
That said, adequately informing clients of risk and dissuading them from nightmare projects they don't realize are nightmare projects yet is doing your clients a service. Sometimes that means advising them to avoid a feature. Sometimes it means implementing something slightly differently. Most of the time it's not even a trade-off. People use JWTs with just user ids in them, but not necessarily for any particular technical/philosophical reason that you have to address first :-)
Hah, yes it was a dumb joke more than anything else. I do want to tip my hat and encourage you to write more of these, they are materially useful as reference in places beyond message board threads and contrived 'worst client ever' quips.
The published asymmetric macaroon constructions were pretty gross last time I looked. We were missing a practical asymmetrically verifiable append only signature. This deficiency rules macaroons out of numerous use cases (namely where the relying party is separate from and untrusted by the issuing party).
The basic idea is what you described: append only asymmetrically verifiable signatures.
As with most things Macaroons, the harder part is developing a caveat language and verifiers that actually meet your needs. And convincing people that they're a good idea.
I think there are a few weird incompatibilities between libraries that are likely to bite you unless you have 1 library you use, but generally speaking: yes, figuring out how to structure your claims is the hard part. Most claims are really quite simple, which is why I’m bullish on most tiny startups just sticking a random token in a database and calling it a day.
I found this part to be one of the most common gotchas of macaroons. As often happens when everybody is "holding it wrong", users are not probably the only ones to be blamed.
That said, macaroons (from what I understood) are meant to be a token that is only verifiable by the issuer.
Third party caveats allow more complex scenarios, but they necessarily involve actual s2s communication to set up (as opposed to the "verification at a distance" allowed by pure asymmetrical crypto)
For example: service A issues a macaroon to service B. Now imagine service B needs to talk to service C, which in turns needs to ensure that B can perform some action on service A. If C could verify A's public key signature, we could stop here.
With macaroons, C needs to issue a macaroon to B, with a third party caveats that requires B to obtain a "discharge macaroon" from A. When B later performs the request on C, it brings both macaroons (the one issued by C with the 3rd party caveat, and the one issued by A, that proves that the caveat is discharged).
C can verify such a macaroon. In particular it can cheaply verify that A is the source of the 3rd party verification.
It’s pretty unweildy compared to HMAC construction because:
* each caveat addition requires local generation of a new asymmetric key pair
* the size of the macaroon grows linearly with the number of caveats. Each new caveat concatenates two asymmetric signatures to the append only signature. Each new caveat adds a public key to the macaroon ID.
* it requires a finalize step for security, meaning the final macaroon extender needs to know it’s the final extender.
It sounded rather impractical but I wouldn’t be surprised if improvements have been made since then.
I don't understand the condemnation of JWTs, this article doesn't seem to explain the condemnation other than saying that "It is extraordinarily easy to screw up JWT." and not to use them.
We use JWTs to provide SSO authentication functionality to partners who wish to take on the responsibility for authenticating their users. I still feel like JWT was the correct choice but I'd really like to know what alluded potential pit falls are.
They provide us with the public key of a asymmetric key pair and we provide them with a key ID to use to identify this key a pair. On our side we associate their key ID(s) with the users they are allowed to authenticate.
When they wish to authenticate a user, they generate a JWT with a user identifier, the client IP address, the Key ID , and the "issued at time". This is then signed using their private key associated with the Key ID. They then provide this to to user's client who then send it us.
We then verify the recency of the JWT, that the JWT is indeed signed by the private key associated with the key id provided, that the IP address matches the client and that the key ID is valid for authenticating the user associated with the user identifier. If all this checks out, we can create a session for the client (using the standard cookie bearer token model).
The reasons we picked JWT are:
1) We aren't responsible for securing their secret(s) (since we never know them) and they can easily send our their public keys to us via less secure channels. If we get a correctly signed JWT, this proves that either the partner approves the authentication or that they have lost control of their private key (in either case, the responsibility is theirs since we have no ability to generate a JWT signed by their private key). This seems like a big improvement over using a shared secret.
2) There are existing libraries for most languages to generate and sign a JWT when provided with a few parameters, this decreases the likely hood that our partners will try to roll their own buggy authentication implementation.
Aside from the issue of trusting our partners not to expose their private key, I'm not sure what the foot-guns are here? (although I am admittedly not an expert)
So what you're saying is that you re-invented SAML, but with JWT? What you're describing is SAML; every commercial SSO system on the market offers it natively.
Our partners are not commercial SSO systems, nor do they all use commercial SSO systems.
We looked at SAML and it didn't seem to be a good fit for our use case. It is overly complex for our needs.
Instead we built a very small subset of what SAML allows, that is very easy to understand and allows our partners to use lightweight JWT libraries with simple APIs instead of trying to figure out how to get a complex SAML implementation to work with our flow.
We didn't re-invent anything. We used an existing, simpler standard for one of it's intended use cases. Our implentation is < 100 lines of code (not including the JWT library which implements the standard).
The model that you described is, verbatim, the bearer model of SAML that everybody uses. SAML in theory does more than just that, but the flow you just described is the subset of SAML that every open source SAML library implements. Apart from some minor security controls that you didn't describe and that SAML implements, the only difference is that you built your ad hoc system using JWE, and SAML is built with XMLDSIG.
(Responding to edit)
I can only assume that the most widely-used flow in SAML, the dominant SSO protocol on the Internet, is better documented than the ad-hoc custom version of SAML you reimplemented with JWT. :)
I don't like SAML (I in fact hate it), but the problems I have with SAML are all shared by JWE/JWT! You are describing perhaps the one scenario in all of human experience where I might recommend that someone adopt an XMLDSIG protocol.
> I can only assume that the most widely-used flow in SAML, the dominant SSO protocol on the Internet, is better documented than the ad-hoc custom version of SAML you reimplemented with JWT. :)
JWT has much better documentation than anything I was able to find on SAML, but this may be due to my lack of experience with the domain. I had trouble even finding clear documentation on what the basic structure of a SAML assertion should be. As a developer with limited knowledge of the domain, when picking a tool to solve my problem I default to the simpler tool with more accessible documentation.
The documentation for utilizing our JWT implementation is simpler and more straightforward than what Salesforce provides for it's similar flow implemented with SAML.
> the problems I have with SAML are all shared by JWE/JWT!
And what are those? This was my original question.
A couple corrections to the section on token binding:
1. It works on all TLS connections, not just mTLS connections. It even works on unauthenticated TLS (although I wouldn't advise forgoing server authentication). That's the beauty of key binding the token. It's useless without the key.
2. It's unclear what the tokbind noun refers to in this paragraph. I'm going to assume that you are just referring to the token binding. A token binding lasts for the duration of a TLS connection (sans renegotiation and resumption which complicate things) and is derived from the [clientrandom,serverrandom,mastersecret] of the connection. The token binding secret is just an RSA or ECDSA keypair, independent of client or server certificates, that is generated when the token is issued. The token is bound to the keypair (e.g. by a hash of the public key that's stored in a JWT claim or stored in a database table keyed by an opaque oauth token).
3. Anyone can use token binding, not just members of the IETF TLS working group :)
I'm fuzzy on token binding versus channel ID, the preceding (and, to my eyes, more useful) extension. In neither case do I anticipate widespread use. But it's good to know that it's there.
Token binding changed a few things as it evolved from origin bound certificates, notably:
* moving from using client certs to signing exported keying material[0] to prove key possession
* adding support for RSA keys
* adding support for multiple token bindings on a single connection (see referred token binding)
Fundamentally the two are very similar. I'm curious as to why you think origin bound certificates are more useful.
> In neither case do I anticipate widespread use
For the browser case:
* 0-RTT in TLS 1.3 (and resumption in general) negate a lot of the benefits of token binding. Big players aren't going to give this up (see QUIC).
* Hardware bound keys are interesting, but if crypto oracles are available on consumer machines, they are often far to
slow to be used practically.
* Many compromises happen in the browser so token binding only marginally improves the security of cookies.
I think it will fall out of favor there. The service to service use case is an interesting one. If you turn off 0-RTT and session resumption, and bind tokens to hardware, the security properties start to look a lot like hardware bound mTLS. If the tooling ever gets developed, it might turn out to be a leaner alternative to a full blown PKI.
I am somewhat surprised by the statements about asymmetric crypto algorithms. Given a good library they don't seem more error prone and given many common use-cases they are not significantly slower.
Securealpolitik: saying there are safe asymmetric primitives doesn’t mean that’s what’s actually deployed. As long as the spec says is P256 ECDSA it’s a pretty reasonable assumption someone is going to screw up nonce handling.
(Incidentally ECDSA really is that much slower, but I appreciate that could be seen as cherry picking because ECDSA is slow even for an asymmetric algorithm.)
I don’t think the argument we’re trying to make is that asymmetric crypto definitionally can’t work. I’m pretty happy TLS exists. Just skeptical that you want to build your s2s auth on it.
FYI: The article seems to make a comment that implies that SPIFFE is only available to Kubernetes, this isnt the case and SPIFFE is explicitly designed for heterogenous environments.
Just hash = hash(secret + salt) and the server enforces the single use by generating and sending one for each authentication, so you need double handshake:
How does the server verify that the salt it receives in the second request is the same salt it generated in the first response? Does the server have to retain state?
Which hash are you using? All this would be for naught if it's one of the many susceptible to length extension attacks; e.g. SHA2. This is the reason everyone uses HMAC now.
I thought the left column above was the client/attacker? She doesn't actually have to use the salt you send her...
[EDIT:] Actually never mind. I'm sure this is all fine, and look how much more efficient this 3-way back-and-forth conversation is than any conventional auth scheme would be. You should design all your own auth.
No wonder it's not well known: it hasn't been picked up by the blog treadmill where dudes on Medium post half-baked info they just found out about, and isn't being pushed by commercial auth proxies.
On that note, posts by Latacora or affiliated persons, there and here, seem to mix well-researched opinions and advice with in-jokes that are lost on all but other experts, assumptions of an inconsistent amount of domain expertise, and quips that muddy some topics more than a bystander would reasonably expect. Why not be more dry and less wry, include links, and morph the FUD around JWT to something real?
[1] https://static.googleusercontent.com/media/research.google.c... [2] https://github.com/rescrv/libmacaroons