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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions