GitHub Advisoryadvisory · PyPI

GHSA-7432-952r-cw78 Authlib Vulnerable to JWE RSA1_5 Bleichenbacher Padding Oracle

HIGH Published 2026-03-16 Modified 2026-03-16 Official page ↗
Aliases
CVE-2026-28490
Ecosystem
PyPI
Packages
authlib
Severity
High
CVSS vector
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N
Weaknesses
CWE-203, CWE-327
Reviewed
2026-03-16

Affected packages

• PyPI authlib: < 1.6.9 → fixed in 1.6.9

Description

1. Executive Summary A cryptographic padding oracle vulnerability was identified in the Authlib Python library concerning the implementation of the JSON Web Encryption (JWE) `RSA1_5` key management algorithm. Authlib registers `RSA1_5` in its default algorithm registry without requiring explicit opt-in, and actively destroys the constant-time Bleichenbacher mitigation that the underlying `cryptography` library implements correctly. When `cryptography` encounters an invalid PKCS#1 v1.5 padding, it returns a randomized byte string instead of raising an exception — the correct behavior per RFC 3218 §2.3.2. Authlib ignores this contract and raises `ValueError('Invalid "cek" length')` immediately after decryption, before reaching AES-GCM tag validation. This creates a clean, reliable Exception Oracle: • Invalid padding → `cryptography` returns random bytes → Authlib length check fails → `ValueError: Invalid "cek" length` • Valid padding, wrong MAC → decryption succeeds → length check passes → AES-GCM fails → `InvalidTag` This oracle is active by default in every Authlib installation without any special configuration by the developer or the attacker. The three most widely used Python web frameworks — Flask, Django, and FastAPI — all expose distinguishable HTTP responses for these two exception classes in their default configurations, requiring no additional setup to exploit. Empirically confirmed on authlib 1.6.8 + cryptography 46.0.5: ``` [PADDING INVALIDO] ValueError: Invalid "cek" length [PADDING VALIDO/MAC] InvalidTag ``` --- 2. Technical Details & Root Cause 2.1 Vulnerable Code File: `authlib/jose/rfc7518/jwe_algs.py` ```python def unwrap(self, enc_alg, ek, headers, key): op_key = key.get_op_key("unwrapKey") # cryptography implements Bleichenbacher mitigation here: # on invalid padding it returns random bytes instead of raising. # Empirically confirmed: returns 84 bytes for a 2048-bit key. cek = op_key.decrypt(ek, self.padding) # VULNERABILITY: This length check destroys the mitigation. # cryptography returned 84 random bytes. len(84) * 8 = 672 != 128 (A128GCM CEK_SIZE). # Authlib raises a distinct ValueError before AES-GCM is ever reached. if len(cek) * 8 != enc_alg.CEK_SIZE: raise ValueError('Invalid "cek" length') # 84 random bytes (cryptography mitigation active) len(84) * 8 = 672 != 128 (CEK_SIZE for A128GCM) raise ValueError('Invalid "cek" length') 16 correct bytes len(16) * 8 = 128 == 128 -> length check passes AES-GCM tag validation -> mismatch raise InvalidTag oracle eliminated ``` Note on timing: The 0.287ms delta is within the noise margin (stdev ~1ms across 50 iterations) and is not claimed as a reliable standalone timing oracle. The exception oracle is the primary exploitable vector and does not require timing measurement. --- 3. Default Framework Behavior — Why This Is Exploitable Out of the Box A potential objection to this report is that middleware or custom error handlers could normalize exceptions to a single HTTP response, eliminating the observable discrepancy. This section addresses that objection directly. The oracle is active in default configurations of all major Python web frameworks. No special server misconfiguration is required. The following demonstrates the default behavior for Flask, Django, and FastAPI — the three most widely deployed Python web frameworks — when an unhandled exception propagates from a route handler: Flask (default configuration) ```python Default Flask behavior — no error handler registered @app.route("/decrypt", methods=["POST"]) def decrypt(): token = request.json["token"] result = jwe.deserialize_compact(token, private_key) # raises ValueError or InvalidTag return jsonify(result) ValueError: Invalid "cek" length -> HTTP 500, body: {"message": "Invalid \"cek\" length"} InvalidTag -> HTTP 500, body: {"message": ""} The exception MESSAGE is different even if the status code is the same. ``` Flask's default error handler returns the exception message in the response body for debug mode, and an empty 500 for production. However, even in production, the response body content differs between `ValueError` (which has a message) and `InvalidTag` (which has no message), leaking the oracle through response body length. FastAPI (default configuration) ```python FastAPI maps unhandled exceptions to HTTP 500 with exception detail in body ValueError: Invalid "cek" length -> {"detail": "Internal Server Error"} (HTTP 500) InvalidTag -> {"detail": "Internal Server Error"} (HTTP 500) ``` FastAPI normalizes both to HTTP 500 in production. However, FastAPI's default `RequestValidationError` and `HTTPException` handlers do not catch arbitrary exceptions, so the distinguishable stack trace is logged — and in many deployments, error monitoring tools (Sentry, Datadog, etc.) expose the exception class to operators, enabling oracle exploitation by an insider or via log exfiltration. Django REST Framework (default configuration) ```python DRF's default exception handler only catches APIException and Http404. ValueError and InvalidTag both fall through to Django's generic 500 handler. In DEBUG=False: HTTP 500, generic HTML response (indistinguishable). In DEBUG=True: HTTP 500, full traceback including exception class (oracle exposed). ``` Summary: Even in cases where HTTP status codes are normalized, the oracle persists through response body differences, response timing, or error monitoring infrastructure. The RFC 3218 §2.3.2 requirement exists precisely because any observable difference — regardless of channel — is sufficient f…

Related

Alias · CVE (1)

  • CVE-2026-28490 Authlib is a Python library which builds OAuth and OpenID Connect servers. vulnerability

Weakness · CWE (2)

  • CWE-203 Observable Discrepancy weakness
  • CWE-327 Use of a Broken or Risky Cryptographic Algorithm weakness

References