CVE-2026-45363 is a critical-severity (CVSS 9.1) Improper Authentication vulnerability in jwt. A fix is available for jwt — see the affected versions and patch details below.
`jwt` (Ruby gem) - empty-key HMAC bypass
Exploitation Status
No confirmed exploitation observed yet
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
- A successful exploit gives an attacker total control of the affected component, not partial access.
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-45363.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-45363 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 380,526 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
jwt💎jwtReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects RubyGems packages — download data is not available via public APIs for these ecosystems.
Description
JWT.decode(token, '', true, algorithm: 'HS256') accepts an attacker-forged token.
OpenSSL::HMAC.digest('SHA256', '', payload) returns a valid digest under an empty key, and no raise InvalidKeyError if key.empty? precondition exists in the HMAC algorithm.
JWT.decode(token, "", true, algorithm: 'HS256')
-> JWA::Hmac.verify(verification_key: "", ...)
-> OpenSSL::HMAC.digest('SHA256', "", signing_input) == signature
The same path is reached when a keyfinder block or key_finder: argument returns "", nil, or an array containing nil for an unknown key. JWT::Decode#find_key only rejects literal nil and empty arrays, and JWT::JWA::Hmac silently coerces nil to "" (signing_key ||= '') before signing.
JWT.decode(token, nil, true, algorithms: ['HS256']) { |_h| "" }
-> find_key returns "" # "" && !Array("").empty? == true
-> JWA::Hmac.verify(verification_key: "", ...)
-> verifies
Common application patterns that produce the unsafe value: redis.get("kid:#{kid}").to_s, ORM string columns with default: '', ENV['SECRET'] || '', Hash.new('') lookups, [primary, fallback] where fallback may be nil. Applications passing a non-empty static key:, or whose keyfinder returns nil / raises on miss, are not affected.
The existing enforce_hmac_key_length option would block this but defaults to false. On OpenSSL ≥ 3.5 the empty-key HMAC.digest call no longer raises, so the OpenSSL-3.0 rescue in JWA::Hmac#sign does not fire.
Affects HS256/HS384/HS512 via both JWT.decode (positional key and block keyfinder) and
JWT::EncodedToken#verify_signature!(key_finder:)
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 💎RubyGems | jwt | ≥ 3.0.0&&< 3.2.0 | 3.2.0bundle update jwt --conservative |
| 💎RubyGems | jwt | all versions | 2.10.3bundle update jwt --conservative |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for jwt, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update jwt to 3.2.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-45363 is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
| Product | Fixed in | Advisory |
|---|---|---|
| Red Hat Satellite 6.16 for RHEL 8 | rubygem-jwt-0:2.10.3-1.el8sat | RHSA-2026:63327 |
| Red Hat Satellite 6.17 for RHEL 9 | ansible-core-1:2.16.19-1.el9sat | RHSA-2026:63387 |
| Red Hat Satellite 6.18 for RHEL 9 | pulpcore-obsolete-packages-0:1.3.1-5.el9pc | RHSA-2026:63386 |
| Red Hat Satellite 6.19 for RHEL 9 | rubygem-jwt-0:2.10.3-1.el9sat | RHSA-2026:63385 |
Frequently Asked Questions
Is CVE-2026-45363 in your dependencies?
Find it across RubyGems, including transitive dependencies.