aio-libs/aiohttp · error · ClientError

Security issue: Digest auth challenge contains empty…

Error message

Security issue: Digest auth challenge contains empty 'nonce' value

What it means

Raised when a Digest challenge contains a 'nonce' key but its value is the empty string. Unlike realm (which RFC 7616 permits empty), an empty nonce breaks replay protection entirely, so aiohttp treats it as a security defect and refuses to proceed. The guard sits in _encode() immediately after nonce is read from the challenge dict.

Solutions

  1. Capture the exact WWW-Authenticate header to confirm nonce is present-but-empty.
  2. Fix the server to generate and emit a real opaque nonce value per RFC 7616.
  3. Report the issue to the server vendor if it is not under your control; do not attempt to weaken the client to accept it.
  4. Fall back to a different auth scheme supported by the server.

Example fix

# before — server sends: WWW-Authenticate: Digest realm="x", nonce=""
await session.get('https://srv/secret')

# after — server must send a populated nonce
# WWW-Authenticate: Digest realm="x", nonce="OA9BSiR4b..."
Defensive patterns

Strategy: try-catch

Validate before calling

import re

def nonce_is_safe(www_authenticate: str) -> bool:
    m = re.search(r'nonce="?([^",\s]+)"?', www_authenticate)
    return bool(m and m.group(1))

Type guard

def nonce_nonempty(challenge_header: str) -> bool:
    import re
    m = re.search(r'nonce="?([^"\s,]*)"?', challenge_header, re.I)
    return m is not None and len(m.group(1)) > 0

Try / catch

from aiohttp import ClientError

try:
    resp = await session.get(url)
except ClientError as e:
    if 'empty' in str(e) and 'nonce' in str(e):
        # security-sensitive: do not weaken; report to server owner
        raise SecurityWarning('Server issued empty Digest nonce')
    raise

Prevention

When it happens

Trigger: Server returns 'WWW-Authenticate: Digest realm="x", nonce=""' (key present, value empty). The middleware reaches the 'if not nonce' check in _encode() and raises ClientError before any hash is computed.

Common situations: Server bug emitting an empty nonce; template/placeholder bug in a generated challenge; security testing where a fuzzer mutates the challenge; proxy that blank-strips unknown fields.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/fa3ff112b1e272e7. Report an issue: GitHub.

Appendix: 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 d041d4d0fd)