From dee174c8c61ecb3d7a449c251c560fac36fcba69 Mon Sep 17 00:00:00 2001 From: Wladimir Braguini Domingues Date: Tue, 19 May 2026 12:17:26 -0300 Subject: [PATCH 1/2] chore: add 1 brand new jwt advisory --- gems/jwt/CVE-2026-45363.yml | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 gems/jwt/CVE-2026-45363.yml diff --git a/gems/jwt/CVE-2026-45363.yml b/gems/jwt/CVE-2026-45363.yml new file mode 100644 index 0000000000..61557c2d9c --- /dev/null +++ b/gems/jwt/CVE-2026-45363.yml @@ -0,0 +1,43 @@ +--- +gem: jwt +cve: 2026-45363 +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 From 234d66444a9aeab9ae17e686eb483c923e5bb169 Mon Sep 17 00:00:00 2001 From: Wladimir Braguini Domingues Date: Tue, 19 May 2026 17:50:22 -0300 Subject: [PATCH 2/2] fix: cr feedback --- gems/jwt/CVE-2026-45363.yml | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/gems/jwt/CVE-2026-45363.yml b/gems/jwt/CVE-2026-45363.yml index 61557c2d9c..b8c96d713b 100644 --- a/gems/jwt/CVE-2026-45363.yml +++ b/gems/jwt/CVE-2026-45363.yml @@ -1,14 +1,17 @@ --- 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')` 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') @@ -16,9 +19,11 @@ description: | -> 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. + 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| "" } @@ -26,12 +31,21 @@ description: | -> 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. + 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. - Affects HS256/HS384/HS512 via both JWT.decode (positional key and block keyfinder) and - `JWT::EncodedToken#verify_signature!(key_finder:)` + 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" @@ -41,3 +55,4 @@ related: - 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