aio-libs/aiohttp · error · ClientError
Security issue: Digest auth challenge contains empty 'nonce'
Error message
Security issue: Digest auth challenge contains empty 'nonce' value
What it means
Raised as a ClientError when a server's HTTP Digest auth challenge returns a 'nonce' parameter that is present but empty (e.g. `nonce=""`). The nonce is the security-critical value used to prevent replay attacks in RFC 7616; an empty nonce makes the digest response trivially replayable, so aiohttp refuses to proceed rather than silently produce a weak credential. Unlike a missing realm/nonce (a malformed challenge), this is flagged specifically as a security issue.
Source
Thrown at aiohttp/client_middleware_digest_auth.py:258
"""
challenge = self._challenge
if "realm" not in challenge:
raise ClientError(
"Malformed Digest auth challenge: Missing 'realm' parameter"
)
if "nonce" not in challenge:
raise ClientError(
"Malformed Digest auth challenge: Missing 'nonce' parameter"
)
# Empty realm values are allowed per RFC 7616 (SHOULD, not MUST, contain host name)
realm = challenge["realm"]
nonce = challenge["nonce"]
# Empty nonce values are not allowed as they are security-critical for replay protection
if not nonce:
raise ClientError(
"Security issue: Digest auth challenge contains empty 'nonce' value"
)
qop_raw = challenge.get("qop", "")
# Preserve original algorithm case for response while using uppercase for processing
algorithm_original = challenge.get("algorithm", "MD5")
algorithm = algorithm_original.upper()
opaque = challenge.get("opaque", "")
# Convert string values to bytes once
nonce_bytes = nonce.encode("utf-8")
realm_bytes = realm.encode("utf-8")
# Use the encoded request-target (raw_path_qs) since that is what is
# transmitted on the wire and what the server signs against. Using the
# decoded form would cause digest verification to fail when the path
# or query string contains percent-encoded reserved characters.
path = URL(url).raw_path_qs
View on GitHub (pinned to c0ef574e29)
Solutions
- Report the bug to the server/proxy operator: their Digest challenge is producing an empty nonce, which is a server-side security defect.
- If you cannot fix the server, disable digest auth for this endpoint and use a different auth mechanism (e.g. bearer token, basic auth over TLS).
- Verify with curl --digest to confirm the server itself is the source of the empty nonce.
- Do not attempt to patch the nonce client-side; an empty nonce defeats replay protection.
Example fix
// before: server returns WWW-Authenticate: Digest realm="x", nonce="", qop="auth"
resp = await session.get(url, auth=DigestAuth(login, pass))
// after: fix server nonce generation, or switch auth
resp = await session.get(url, headers={"Authorization": "Bearer <token>"}) Defensive patterns
Strategy: try-catch
Try / catch
from aiohttp import ClientError
try:
await session.get(url, auth=DigestAuth(login, pwd))
except ClientError as e:
if "empty 'nonce'" in str(e):
# server bug; fall back to a different auth method
... Prevention
- Do not treat empty-nonce servers as compatible; lobby the operator to fix nonce generation.
- When integrating with unknown Digest servers, probe with curl --digest first.
- Wrap digest-auth requests in a ClientError handler that logs the raw WWW-Authenticate header.
When it happens
Trigger: The error fires in the digest-auth middleware during challenge parsing (line 257-260), after 'nonce' is confirmed present (line 247) but `not nonce` evaluates True (line 257). It triggers on any 401 response whose WWW-Authenticate digest header contains `nonce=""`.
Common situations: Hitting a misconfigured reverse proxy, load balancer, or custom auth filter that emits a Digest challenge with an empty nonce; old/buggy server-side nonce generators; man-in-the-middle appliances that strip nonce values.
Related errors
- Digest auth error: Unsupported Quality of Protection (qop) v
- Malformed Digest auth challenge: Missing 'nonce' parameter
- Digest auth error: Unsupported hash algorithm: {algorithm}.
- 1009
- 1009
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/fa3ff112b1e272e7.json.
Report an issue: GitHub.