Add enforce_hmac_key_length configuration option#716
Conversation
8a2e20e to
c6b8232
Compare
| algorithms: algorithms, | ||
| required_claims: required_claims | ||
| required_claims: required_claims, | ||
| hmac_min_key_length: hmac_min_key_length |
There was a problem hiding this comment.
Could we instead of letting users set the min length just let the gem validate against the recommended minimums, so just enable or disable the feature. Then in future versions we could enable it by default. Something like enforce_hmac_key_length
I think this would be a bit less complex and easier for users to toggle on.
There was a problem hiding this comment.
Or is there a use-case where you would like to enforce a higher minimum than the default...
There was a problem hiding this comment.
Good point! I initially considered making it configurable, but default minimums should be sufficient.
I’ve pushed new changes to the branch using the enforce_hmac_key_length boolean flag instead.
Thanks for the review, @anakinj! Could you please take another look?
| class Hmac | ||
| include JWT::JWA::SigningAlgorithm | ||
|
|
||
| MIN_KEY_LENGTHS = { |
There was a problem hiding this comment.
Future us could wonder where are these values coming from, a comment pointing to the RFC would be useful at this point.
Add boolean toggle to enforce minimum HMAC key lengths based on RFC 7518 Section 3.2 (HS256: 32, HS384: 48, HS512: 64 bytes). Default is false (opt-in), allowing future versions to enable by default.
c6b8232 to
27a6325
Compare
nevans
left a comment
There was a problem hiding this comment.
This is great, thanks! I have a few "drive by" comments. If you think they're reasonable, I can convert them into a PR.
| min_length = MIN_KEY_LENGTHS[alg] | ||
| return if key.bytesize >= min_length |
There was a problem hiding this comment.
This should guard against nil to future proof against unknown algorithms, right?
Should it be permissive for unknown algorithms?
| min_length = MIN_KEY_LENGTHS[alg] | |
| return if key.bytesize >= min_length | |
| min_length = MIN_KEY_LENGTHS[alg] | |
| return if minkey_length &.<= key.bytesize |
IMO, that's not appropriate. It just leaves open a vector for potential security vulnerabilities.
Should it enforce that new algorithms are also added to MIN_KEY_LENGTHS?
| min_length = MIN_KEY_LENGTHS[alg] | |
| return if key.bytesize >= min_length | |
| min_length = MIN_KEY_LENGTHS.fetch(alg) | |
| return if key.bytesize >= min_length |
Alternatively, you can just ask the digest what its length should be:
| min_length = MIN_KEY_LENGTHS[alg] | |
| return if key.bytesize >= min_length | |
| min_length = MIN_KEY_LENGTHS.fetch(alg) { digest.new.digest_length } | |
| return if key.bytesize >= min_length |
You could also avoid the extra allocation by instantiating digest.new prior to calling validate_key_length! and pass in min_length: digest_obj.digest_length as an argument. Then you could also simplify it so the MIN_KEY_LENGTHS hash is completely unnecessary:
| min_length = MIN_KEY_LENGTHS[alg] | |
| return if key.bytesize >= min_length | |
| return if key.bytesize >= min_length |
I'd personally vote for that last option.
| min_length = MIN_KEY_LENGTHS[alg] | ||
| return if key.bytesize >= min_length | ||
|
|
||
| raise_verify_error!("HMAC key must be at least #{min_length} bytes for #{alg} algorithm") |
There was a problem hiding this comment.
IMO, although it's reasonable to preserve backwards compatibility, this should at least warn by default. This may be insignificant in development or CI, but it could be very significant in production. Also, I always appreciate when breaking changes are preceded by a warning period.
Personally, I'd use enforce_hmac_key_length = :warn (vs other truthy values) for warnings. But you could also use nil to indicate unset/default (to warn instead of raise) vs false for explicitly set (to opt-out of warnings).
| raise_verify_error!("HMAC key must be at least #{min_length} bytes for #{alg} algorithm") | |
| msg = "HMAC key must be at least #{min_length} bytes for #{alg} algorithm" | |
| if JWT.configuration.decode.enforce_hmac_key_length == :warn | |
| warn msg | |
| else | |
| raise_verify_error!(msg) | |
| end |
There was a problem hiding this comment.
If you have time to look into these I would suggest we swap the default to warn already in the next release.
|
@nevans Your suggestions sounds reasonable. I'd be happy to see a PR with the improvements. |
Description
Add
enforce_hmac_key_lengthconfiguration option to enforce minimum key lengths for HMAC algorithms. This is a security hardening measure that helps prevent brute-force attacks on weak keys.Recommended minimum key lengths per RFC 7518 Section 3.2:
See: https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2
Usage:
For backward compatibility, it's disabled by default (false). But it would be nice to enforce it in a future major version like it's done for RSA key validation (minimum 2048 bits). For now, users must explicitly opt-in.
Checklist
Before the PR can be merged be sure the following are checked: