Just pick a crypto scheme and the JWT is just an encoding that makes it easier to use.
(It's just a more convenient way of rolling your own scheme)
That said: random tokens have a lot going for them :)
Just pick a crypto scheme and the JWT is just an encoding that makes it easier to use.
(It's just a more convenient way of rolling your own scheme)
That said: random tokens have a lot going for them :)
> Just pick a crypto scheme and the JWT is just an encoding that makes it easier to use.
That's not what JWT is, but I can understand why someone would be misled into believing that.
JWT isn't just an encoding format, it also includes a crypto algorithm negotiation protocol that lets the attacker choose the algorithm. Even if you strictly allow-list which algorithm you want to support, you can accidentally bypass this control in many libraries if you support the `kid` (key ID) header. [1]
It also allows attackers to completely strip the security. [2] [3]
Put shortly, JWT is a gun aimed directly at your foot. That's why there's so much hate for JWTs.
[1] https://github.com/firebase/php-jwt/issues/351
[2] https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...
If you control both sides, then you can ignore this part or do it out-of-band.
Though, if you control both sides, then you can use literally anything else too.
This is the norm.
As for the key ID attack, this sounds like it's just a trick to know where the private key is located? It shouldn't be publicly accessible.
https://blog.pentesteracademy.com/hacking-jwt-tokens-kid-cla...
Every JWT proponent says that, but it's a misuse that shows up in multiple libraries, in multiple languages, and isn't explicitly called out in the JWT Best Practices RFC at all.
I'm going to blame the standard for being error-prone.
There's nothing in any JWT RFC, to date, that calls out the need for cryptographic keys to be the raw key material in addition to its parameter choices, rather than just the raw key material. That's a fault of the standard.
That's not a single library's fault. That's the standard's fault.
PASETO has this to say: https://github.com/paseto-standard/paseto-spec/blob/master/d...
> As for the key ID attack, this sounds like it's just a trick to know where the private key is located? It shouldn't be publicly accessible.
This doesn't involve private keys at all. Look at the proof of concept code. https://github.com/firebase/php-jwt/files/6966712/php-jwt-po...
{
"my-super-cool-key-id": "some hs256 secret key string goes here",
"another-key-id": "-----BEGIN RSA PUBLIC KEY-----\n ... snip ...",
"yet-another-key-id": "----BEGIN EC PUBLIC KEY-----\n... snip ..."
}
And let's say you've got three different API endpoints, which each hard-code a specific algorithm (one HS256, one PS256, one ES256). But, because of the framework you're developing in, you're expected to provide a single configuration object containing a map of key IDs used by the entire application. (This is a common framework quirk.)What stops you from swapping out the kid in a JWT's header and getting the underlying library to use the wrong key type for the endpoint that accepts HS256?
{
"alg": "HS256",
- "kid": "my-super-cool-key-id",
+ "kid": "yet-another-key-id",
"typ": "JWT"
}
The answer varies per implementation. The JWT standards do not call this misuse potential out at all.Strictly listing the keys does not, at all, hard-code the algorithm those keys are used with in every possible programming language and runtime.
Some languages (Java) accidentally prevent this through type safety in the low-level crypto APIs. Others accidentally prevent this by not supporting the kid header.
I have yet to see a JWT library that deliberately prevents this misuse potential. Is that the library's fault? Or their defaults' fault?
EDIT:
The fix, by the way, requires doing this:
{
"my-super-cool-key-id": {
"alg": "HS256",
"key": "some hs256 secret key string goes here"
},
"another-key-id": {
"alg": "PS256",
"key": "-----BEGIN RSA PUBLIC KEY-----\n ... snip ..."
},
"yet-another-key-id": {
"alg": "ES256",
"key": "----BEGIN EC PUBLIC KEY-----\n... snip ..."
}
}
And then verifying that the alg for the key matches the alg for the token before attempting to verify the signature/MAC.https://groups.google.com/g/django-developers/c/6oS9R2GwO4k/... - on the Django mailing list
https://www.zofrex.com/blog/2020/10/20/alg-none-jwt-nhs-cont... - where the punchline is "Writing the code to sign data with a private key and verify it with a public key would have been easier to get correct than correctly invoking the JWT library. In fact, the iOS app (which gets this right) doesn’t use a JWT library at all, but manages to verify using a public key in fewer lines of code than the Android app takes to incorrectly use a JWT library!"