Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions gems/jwt/CVE-2026-45363.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
gem: jwt
cve: 2026-45363
notes: 'CVE has been reserved, but not filled in.'
ghsa: c32j-vqhx-rx3x
url: https://github.com/jwt/ruby-jwt/security/advisories/GHSA-c32j-vqhx-rx3x
title: 'ruby-jwt: Empty-key HMAC bypass; cross-language sibling of CVE-2026-44351'
date: 2026-05-18
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:)`.
cvss_v3: 7.4
patched_versions:
- ">= 3.2.0"
related:
url:
- https://github.com/jwt/ruby-jwt/security/advisories/GHSA-c32j-vqhx-rx3x
- https://github.com/jwt/ruby-jwt/commit/db560b769a07bd9724e77ff505011ac01872106f
- https://github.com/jwt/ruby-jwt/releases/tag/v3.2.0
- https://github.com/advisories/GHSA-c32j-vqhx-rx3x
- https://www.cve.org/CVERecord?id=CVE-2026-45363
Loading