Skip to content

Auth challenge handling: repeated WWW-Authenticate, Digest -sess without qop, empty nonce/realm #74

Description

@Wahbeh-Mohammad

Part of the audit remediation umbrella #67. Milestone 3. Severity: HIGH. Requirements: AUTH-12, AUTH-16, AUTH-17, AUTH-22, AUTH-25.

Current SDK behavior

Only the first challenge header is read. pickChallengeHeader
(packages/core/src/auth/auth-step.ts:420,423) calls response.headers.get(...), which
returns the first value. @dexpace/transport-undici delivers a repeated WWW-Authenticate
as an array, and undici-transport.ts:334-335 pushes each element as its own entry. Every
challenge after the first is discarded. A 401 whose first line is
Digest … algorithm=SHA-512-256 (unsupported) and whose second is
Digest … algorithm=SHA-256, qop="auth" ends with one send, status 401, no Authorization.
The same two challenges in one comma-joined value end with two sends and status 200.
RFC 7616 §3.3 recommends one header per algorithm. @dexpace/transport-fetch comma-joins, so
it is unaffected. The comment at undici-transport.ts:334 says arrays are for Set-Cookie
only; undici arrays any repeated header.

-sess without qop sends an unverifiable response. A challenge with
algorithm=MD5-sess (or SHA-256-sess) and no qop is accepted (digest.ts:250-258). HA1 is
H(H(u:r:p):nonce:cnonce) with a fresh random cnonce (:337-343), but cnonce is written to
the header only when qop=auth is present (:386-387). The server cannot recompute HA1.
Every such request fails, and AUTH-30 bounds the replay to one 401.

Empty nonce / realm count as present. digest.ts:246-248 tests === undefined. A
truncated WWW-Authenticate: Digest realm="r", nonce= yields nonce: '', is accepted, and the
client sends nonce="".

Expected behavior

The auth step sees every challenge the server sent, in order, whichever transport delivered
the response. ChallengeHandler.rank selects across all of them. A Digest challenge the SDK
cannot answer verifiably is declined and the next challenge is tried. A challenge with an
empty realm or nonce is not satisfiable.

Notes and leads

  • pickChallengeHeader: use headers.getAll(...) and either join with , before parsing or
    parse each value and concatenate the challenge lists. RFC 9110 §11.6.1 permits both wire
    shapes. Same for Proxy-Authenticate.
  • -sess without qop: RFC 7616 §3.4.2 requires cnonce for -sess regardless of qop.
    Emitting cnonce is what servers expect. AUTH-22 as written forbids it. Decide, then record
    the reading in the Phase 5c ledger section. The alternative, declining the challenge, needs no
    ledger row.
  • Empty values: require non-empty strings for realm and nonce.
  • Tests: core test with two WWW-Authenticate entries; a conformance row in
    packages/transport-conformance asserting both transports surface repeated challenge headers
    identically; computeDigestResponse vector for MD5-sess with no qop; empty nonce.
  • Fix the undici comment. Patch changeset for @dexpace/core.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:authAuthentication and credentials: AUTH-*area:coreCore HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*area:resilienceRetry, recovery, redirects: RETRY-* RECOV-* REDIR-*type:bugDefect in shipped behavior

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions