3 ms·
If I give you a map of kid -> key material that looks like this: { "my-super-cool-key-id": "some hs256 secret key string goes here", "another-key
by CiPHPerCoder 5y ago
If I give you a map of kid -> key material that looks like this:
{
"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.