{"id":"CVE-2026-45363","aliases":["GHSA-c32j-vqhx-rx3x"],"url":"https://o3.security/vulnerability/CVE-2026-45363","summary":"`jwt` (Ruby gem) - empty-key HMAC bypass","details":"`JWT.decode(token, '', true, algorithm: 'HS256')` accepts an attacker-forged token.\n`OpenSSL::HMAC.digest('SHA256', '', payload)` returns a valid digest under an empty key, and no `raise\n  InvalidKeyError if key.empty?` precondition exists in the HMAC algorithm.\n\n```\nJWT.decode(token, \"\", true, algorithm: 'HS256')\n  -> JWA::Hmac.verify(verification_key: \"\", ...)\n  -> OpenSSL::HMAC.digest('SHA256', \"\", signing_input) == signature\n```\n\nThe same path is reached when a keyfinder block or key_finder: argument returns \"\", nil, or an\narray containing nil for an unknown key. JWT::Decode#find_key only rejects literal nil and empty\narrays, and JWT::JWA::Hmac silently coerces nil to \"\" (signing_key ||= '') before signing.\n\n```\nJWT.decode(token, nil, true, algorithms: ['HS256']) { |_h| \"\" }\n  -> find_key returns \"\"               # \"\" && !Array(\"\").empty? == true\n  -> JWA::Hmac.verify(verification_key: \"\", ...)\n  -> verifies\n```\nCommon 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.\n\nThe 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.\n\nAffects HS256/HS384/HS512 via both JWT.decode (positional key and block keyfinder) and\n`JWT::EncodedToken#verify_signature!(key_finder:)`","published":"2026-07-14T21:32:26.676Z","modified":"2026-09-20T11:31:04.655964466Z","cvss":{"score":9.1,"severity":"CRITICAL","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"},"epss":{"score":0.00242,"percentile":0.15478,"asOf":"2026-08-14"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"RubyGems","name":"jwt","fixedVersion":"3.2.0"},{"ecosystem":"RubyGems","name":"jwt","fixedVersion":"2.10.3"}],"fix":{"url":"https://github.com/jwt/ruby-jwt/commit/9820020869ad147b941e49d96ab8beba35532964","label":"jwt/ruby-jwt@9820020"},"references":[{"type":"WEB","url":"https://github.com/jwt/ruby-jwt/releases/tag/v2.10.3"},{"type":"WEB","url":"https://github.com/jwt/ruby-jwt/releases/tag/v3.2.0"},{"type":"WEB","url":"https://lists.debian.org/debian-lts-announce/2026/09/msg00022.html"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45363.json"},{"type":"ADVISORY","url":"https://github.com/jwt/ruby-jwt/security/advisories/GHSA-c32j-vqhx-rx3x"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-45363"},{"type":"FIX","url":"https://github.com/jwt/ruby-jwt/commit/9820020869ad147b941e49d96ab8beba35532964"},{"type":"FIX","url":"https://github.com/jwt/ruby-jwt/commit/db560b769a07bd9724e77ff505011ac01872106f"},{"type":"WEB","url":"https://github.com/jwt/ruby-jwt/issues/724"},{"type":"PACKAGE","url":"https://github.com/jwt/ruby-jwt"},{"type":"WEB","url":"https://github.com/rubysec/ruby-advisory-db/blob/master/gems/jwt/CVE-2026-45363.yml"},{"type":"WEB","url":"https://www.cve.org/CVERecord?id=CVE-2026-45363"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-20T11:31:04.655964466Z"}}