GitHub Advisoryadvisory · PyPI
GHSA-7432-952r-cw78 Authlib Vulnerable to JWE RSA1_5 Bleichenbacher Padding Oracle
HIGH
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
References
- https://github.com/authlib/authlib/security/advisories/GHSA-7432-952r-cw78 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-28490 advisory
- https://github.com/authlib/authlib/commit/48b345f29f6c459f11c6a40162b6c0b742ef2e22 web
- https://github.com/authlib/authlib package
- https://github.com/authlib/authlib/releases/tag/v1.6.9 web