Executive summary
A critical bug in the Ruby jwe gem (JSON Web Encryption) allows attackers to bypass AES-GCM authentication and tamper with or decrypt JWE tokens. The issue stems from missing validation of the authentication tag; attackers can brute-force or truncate the tag, enabling forged ciphertexts and even recovery of the GCM GHASH subkey under some conditions. Impacted versions: ≤ 1.1.0. Fixed in 1.1.1. Key rotation is required because the GHASH key may have leaked. CVSS v3.1: 9.1 (Critical). GitHubNVD
What exactly broke?
AES-GCM provides both confidentiality and integrity: decryption must verify a 128-bit authentication tag before releasing plaintext. The JWE spec mandates a 128-bit tag for A128GCM/A192GCM/A256GCM. The vulnerable Ruby implementation failed to enforce the tag length/validation correctly, letting attackers submit shortened tags that are feasible to brute-force. IETF DatatrackerGitHub
Practical effects confirmed by the advisory:
- Modify a JWE to decrypt to an attacker-chosen value (integrity break).
- Decrypt a JWE by observing parsing differences (oracle-style confidentiality loss).
- Recover the GCM internal GHASH key, enabling broader forgery with the same key. GitHub
Why rotate keys if you never used GCM? The CNA/NVD notes that users are affected even if they don’t currently use AES-GCM, because the GHASH key may already have leaked; rotate the underlying encryption keys after upgrading. NVD
A quick refresher: JWE + AES-GCM
A compact JWE is:BASE64URL(header).BASE64URL(iv).BASE64URL(ciphertext).BASE64URL(tag)
For AES-GCM in JWE:
- IV size: 96 bits (12 bytes) — required.
- Tag size: 128 bits (16 bytes) — required.
Not meeting these invariants must be rejected. IETF Datatracker
Attack paths (defender’s view)
- Tag truncation + brute force
If the library accepts tags <16 bytes, an attacker crafts ciphertexts and iterates over the reduced tag space until decryption “succeeds” — turning GCM’s strong integrity into a guessable check. (This is precisely what the fix closes by enforcing tag length.) GitHub - Parsing-difference oracle
With partial tag bypass, adversaries can distinguish parser errors vs. downstream decoding errors, leaking information and eventually recovering plaintext. GitHub - GHASH subkey recovery
Given sufficient chosen inputs and acceptance of malformed tags, an adversary can derive H = AES_K(0^128) (the GHASH key), enabling counterfeit tags for additional messages under that key. Result: broad token forgery risk. GitHubNVD
Impacted surface
- Ruby apps that use the
jwegem for: encrypted JWTs, session cookies, SSO assertions, service-to-service tokens, or field-level data protection. - Any upstream gateway/service that trusts these JWEs for authZ or data integrity. (This can cascade into privilege escalation if tokens are accepted as valid.) GitHub
Detection & hunting
Quick content checks at the edge
- Reject JWEs with
encin{A128GCM,A192GCM,A256GCM}and a tag length ≠ 16 bytes after Base64URL decode. This aligns with RFC 7518. IETF Datatracker
Telemetry clues
- Spikes in JWE decryption failures around early August 2025 (probing/exploitation).
- Tokens repeatedly failing policy checks but passing app-level parsing — look for inconsistent error sources.
Log analysis heuristic
- For APIs that log decode stages, alert on “decryption success” + “schema/JSON error” combos, a sign of attacker-crafted plaintext designed to traverse parsing layers.
Remediation (do this now)
- Patch: Upgrade
jweto 1.1.1 (adds mandatory tag length check).rubyCopyEdit# Gemfile gem "jwe", "~> 1.1.1" # then bundle update jweGitHub - Rotate keys immediately: Assume the GHASH key (and thus integrity of messages under that key) may be compromised. Generate new symmetric keys, publish a new
kid, and invalidate old tokens aggressively. NVD - Force re-authentication for user sessions that relied on JWE. (Don’t trust long-lived tokens minted before the fix+rotation.)
- Gateway enforcement: At your API gateway/WAF, enforce
tag.length == 16for all GCM-JWEs; drop on mismatch. (This mirrors the RFC requirement.) IETF Datatracker - SBOM & CI: Add
bundler-audit(or equivalent) to CI, failing builds on CVE-2025-54887 for versions ≤ 1.1.0. Track runtime gems via SBOM and verify production parity with CI.
Validation after the fix
- Unit tests: Attempt to decrypt a JWE with a 1-byte tag; expect failure.
- Interoperability: Cross-decrypt with another JOSE library (e.g., Nimbus JOSE) that always uses 128-bit tags to confirm behavior matches spec. GitHubjavadoc.io
Governance notes for security leaders
- Risk: Critical (remote, no auth, data integrity + confidentiality at stake). Confirmed by CNA/NVD with CVSS 9.1. GitHub
- SLA: 24–48h to patch+rotate for external-facing systems.
- Attestation: Record evidence of rotation (new
kid, cutoff time) and purge of pre-fix tokens. - Policy: Require spec-level invariants (e.g., AES-GCM 128-bit tags) as gateway checks, not just library defaults.
References
- GitHub Advisory (GHSA-c7p4-hx26-pr73) — root cause, impact, fix 1.1.1, rotate keys. GitHub
- NVD CVE-2025-54887 — CNA summary, CVSS 9.1, key-rotation guidance. NVD
- RubySec — ecosystem advisory reiterating tag-length fix and rotation. RubySec
- Tenable — vendor summary of attack consequences. Tenable®
- RFC 7518 (JWA) — JWE must use a 128-bit tag for AES-GCM. IETF Datatracker
Leave a comment