Skip to content

Add enforce_hmac_key_length configuration option#716

Merged
anakinj merged 1 commit into
jwt:mainfrom
304:fix/hmac-min-key-length
Feb 15, 2026
Merged

Add enforce_hmac_key_length configuration option#716
anakinj merged 1 commit into
jwt:mainfrom
304:fix/hmac-min-key-length

Conversation

@304

@304 304 commented Feb 14, 2026

Copy link
Copy Markdown
Contributor

Description

Add enforce_hmac_key_length configuration 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:

  • HS256: 256 bits (32 bytes)
  • HS384: 384 bits (48 bytes)
  • HS512: 512 bits (64 bytes)

See: https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2

Usage:

  JWT.configuration.decode.enforce_hmac_key_length = true

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:

  • There are tests for the fix or feature added/changed
  • A description of the changes and a reference to the PR has been added to CHANGELOG.md. More details in the CONTRIBUTING.md

@304
304 force-pushed the fix/hmac-min-key-length branch 2 times, most recently from 8a2e20e to c6b8232 Compare February 14, 2026 16:14
algorithms: algorithms,
required_claims: required_claims
required_claims: required_claims,
hmac_min_key_length: hmac_min_key_length

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is there a use-case where you would like to enforce a higher minimum than the default...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment thread lib/jwt/jwa/hmac.rb
class Hmac
include JWT::JWA::SigningAlgorithm

MIN_KEY_LENGTHS = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@304
304 force-pushed the fix/hmac-min-key-length branch from c6b8232 to 27a6325 Compare February 15, 2026 10:12
@304 304 changed the title Add configurable HMAC minimum key length Add enforce_hmac_key_length configuration option Feb 15, 2026

@anakinj anakinj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff, thanks for the addition. Next major release should swap the default to true

@anakinj
anakinj merged commit d3e52e9 into jwt:main Feb 15, 2026
15 checks passed

@nevans nevans left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thanks! I have a few "drive by" comments. If you think they're reasonable, I can convert them into a PR.

Comment thread lib/jwt/jwa/hmac.rb
Comment on lines +56 to +57
min_length = MIN_KEY_LENGTHS[alg]
return if key.bytesize >= min_length

@nevans nevans Jul 1, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should guard against nil to future proof against unknown algorithms, right?

Should it be permissive for unknown algorithms?

Suggested change
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?

Suggested change
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:

Suggested change
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:

Suggested change
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.

Comment thread lib/jwt/jwa/hmac.rb
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")

@nevans nevans Jul 1, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have time to look into these I would suggest we swap the default to warn already in the next release.

@anakinj

anakinj commented Jul 1, 2026

Copy link
Copy Markdown
Member

@nevans Your suggestions sounds reasonable. I'd be happy to see a PR with the improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants