You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Thank you very much for contributing this library. All of your modules are high quality.
This module is potentially vulnerable to an algorithm/key mismatch attack. In summary, if a token producer and consumer have a mutual understanding that they will sign/verify tokens using RSA and not HMAC, it is possible in some cases to generate forged tokens to bypass token verification. I've been reading around and it seems like people are getting around this by including the fingerprint of the RSA key in the header table when signing. The verifier then validates this fingerprint against the calculated fingerprint that it already has.
Here is a busted test for jwt_spec.lua that demonstrates the problem.
it("is not vulnerable to algorithm/key mismatch attack", function()
localkeyPair=crypto.pkey.generate("rsa", 512)
-- The known trusted key between priver and audience-- Distributed to audience that expects RSA signed tokens---- Perhaps I'm running a service and use a separate auth-- provider. They've given me this public key for token-- validation. Or the public key is known publicly.---- Pre-requisite for this issueknown_trusted_public_key=keyPair:to_pem()
-- Generate a legitimate token for our audiencefunctionservice_sign_token(claims)
localkeys= {
private=keyPair
}
returnjwt.encode(claims, { alg="RS256", keys=keys })
end-- Verify rsa tokens from our provider. Because....-- I use a provider that only generates rsa tokens?---- In the real world, this is likely not written in the-- same language as our signer, let alone the same lib.---- This makes this test a bit contrived but only trying-- to demonstrate the flaw. In this case, this lib is-- only safe from this attack if the user is feeding-- a crypto.pkey public key object to the decoder.---- And thats only because crypto.hmac.digest can not-- handle userdata arguments for key.functionvalidate_token_from_service(token)
returnjwt.decode(token, { keys= { public=known_trusted_public_key }})
end-- Claim to something private that only the admin should have access tolocalclaims= {
claim_to_access_customer_data=true
}
locallegit_token=service_sign_token(claims)
assert.truthy(legit_token~=nil)
locallegit_claim, err=validate_token_from_service(legit_token)
assert.same(claims, legit_claim) -- all good-- Now someone has gotten a hold of the public key. Maybe it's sitting somewhere public for web traffic.-- Generate a forged packet and bi-pass the provider entirelylocalforged_token=jwt.encode(claims, {alg="HS256", keys= { private=known_trusted_public_key }})
forged_claim, err=validate_token_from_service(forged_token)
assert.are_not.same(claims, forged_claim)
end)
The text was updated successfully, but these errors were encountered:
I just realized this is a duplicate of #2. The above unit test indicates this is still an issue. Am I misunderstanding something?
The article linked in #2 covers 2 common security issues. I think the first is related to plain tokens and it looks like that was fixed here. Am I testing against the wrong version?
I will be implementing JWT in one of my projects and would love to use this module but I may have to fork to avoid this issue because I won't be writing the consumer, only the provider. If I end up forking, do you want a PR?
I think it can be fixed by overriding the algorithm specified in the header when you really know which one should be used. It'll take the following modifications:
src/jwt/jws.lua::76ifnotself.verify[options.algorheader.alg](rawHeader.."."..bodyStr, signature, options.keys.public) then
Then, in the test it will become
functionvalidate_token_from_service(token)
returnjwt.decode(token, { keys= { public=known_trusted_public_key }, alg="RS256"})
end
Also, the test should be a bit modified at the point of keyPair generation since there's an assertion that the type of the key is "string" and not "userdata" which is generated by the crypto.pkey.generate
Hello,
Thank you very much for contributing this library. All of your modules are high quality.
This module is potentially vulnerable to an algorithm/key mismatch attack. In summary, if a token producer and consumer have a mutual understanding that they will sign/verify tokens using RSA and not HMAC, it is possible in some cases to generate forged tokens to bypass token verification. I've been reading around and it seems like people are getting around this by including the fingerprint of the RSA key in the header table when signing. The verifier then validates this fingerprint against the calculated fingerprint that it already has.
Here is a busted test for jwt_spec.lua that demonstrates the problem.
The text was updated successfully, but these errors were encountered: