skip to content

In HTTP Digest Authentication, what roles do the server nonce, the client-generated cnonce and the nc counter play in replay protection, and what does a 401 challenge carrying `stale=true` mean?

level: seniorimportance: nice to knowfreq 20%

answer

  1. nonce = timestamp + MAC(server secret)
  2. nc increases, server keeps the high-water mark
  3. cnonce = client entropy vs chosen-plaintext
  4. stale=true → retry, do not re-prompt
  5. no nc tracking → replay window = nonce lifetime

basics

~20 s

The server nonce makes each challenge unique and expirable; nc counts requests under that nonce so the server can reject a repeat; cnonce is client entropy that stops the server from choosing all hash inputs. stale=true means the nonce expired but the password was correct, so the client retries silently without re-prompting.

solid answer

~50 s

Replay protection is layered. The **server nonce** is issued per challenge, usually encoding a timestamp plus a MAC over a server secret, so the server can age it out and detect forgery without keeping a table. The **nc** counter is an 8-hex-digit value the client increments for every request reusing that nonce; a server that remembers the highest nc seen rejects any request whose nc repeats or goes backwards — that is the actual anti-replay check. The **cnonce** is chosen by the client and mixed into the digest so a hostile server cannot pick every input and mount a chosen-plaintext or precomputed-dictionary attack against the password. When a nonce ages out, the server returns another `401` with `stale=true`. That distinguishes "your credential is fine, my nonce expired" from "wrong password": the client silently recomputes with the new nonce instead of re-prompting the user. Servers that never track nc, or that accept unbounded nonce lifetimes, get little real replay protection.

go deeper

for a junior

Know that the nonce makes each challenge one-off and that the client adds its own random cnonce. The counter detail can wait.

for a middle

Explain all three values and be able to say which one actually detects a replay (nc, and only if the server tracks it).

for a senior

Discuss nonce lifetime versus round trips, stateless nonce construction behind a load balancer, and the fact that many servers skip nc tracking entirely.

for a principal

Position the whole mechanism as raising the cost of credential capture only, and argue that transport security plus short-lived tokens dominate it on every axis.

## What replay means here On a cleartext connection an attacker can copy an entire `Authorization: Digest` header. Since the digest is a fixed string, resending it would authenticate the attacker unless something makes each credential single-use. Three values do that work, and none is sufficient alone. ## Server nonce The nonce is opaque to the client but structured on the server. A common construction is `base64(timestamp : MAC(server-secret, timestamp [, client-ip]))`. That lets the server decide two things from the nonce alone: was it issued by me, and how old is it — with no shared session table, which matters behind a load balancer where the next request may hit a different node. Nonce lifetime is a direct tradeoff: short lifetimes shrink the replay window but force extra 401 round trips; long lifetimes push all the protection onto nc tracking. ## The nc counter `nc` (nonce count) is an 8-hex-digit counter starting at `00000001`, incremented by the client for every request that reuses the same nonce, and folded into the digest — so it cannot be edited without invalidating the hash. The server keeps, per nonce, the highest nc it has accepted, and rejects any request whose nc is less than or equal to it. **This is the only mechanism that actually detects a replay within a nonce's lifetime.** A server that ignores nc — and many do — leaves every captured credential replayable until the nonce expires. Tracking nc reintroduces exactly the per-nonce server state that a stateless nonce was meant to avoid, which is one reason implementations skip it. ## The cnonce The client nonce protects the *client*, not the server. Without it, the server controls every input to the hash (nonce, and it knows realm and URI), so a malicious or compromised server can issue a chosen nonce and use the reply to attack the password offline — including with precomputed tables. Mixing in fresh client-side randomness makes each digest unpredictable to the server and defeats precomputation. The `-sess` algorithm variants go further by folding nonce and cnonce into A1 itself, so the derived session key changes per authentication. ## stale=true When a request arrives with a well-formed digest that verifies against an *expired* nonce, the server should not report a credential failure. It returns `401` with `WWW-Authenticate: Digest ..., nonce="<new>", stale=true`. The semantics defined by RFC 7616 are: the credential was correct, only the nonce is out of date. A browser therefore recomputes with the new nonce and retries without showing the password prompt again; without `stale=true` it would treat the 401 as a rejected password and re-prompt, which is both bad UX and a nudge toward users retyping passwords into anything that asks. When `stale` is absent or false, the client must obtain a new username and password. ## Where the protection ends All of this defends the credential, not the exchange. The request body, the response and every other header travel in the clear. An active attacker who can modify traffic can still tamper with a request body under `qop=auth`, and can strip the Digest challenge and offer a Basic one instead — a downgrade the protocol has no defence against. Digest's replay machinery only ever raised the cost of *credential* theft; confidentiality and integrity require TLS.

  • If a server never tracks nc values, is Digest still protected against replay?
    Only weakly. Without nc tracking, a captured Authorization header stays valid for the whole lifetime of the nonce it was built with, so the replay window equals the nonce expiry. The only remaining mitigations are short nonce lifetimes and binding the nonce to a client IP, both of which cost round trips or break under NAT and proxies.
  • Why can't a server just store issued nonces in memory?
    It can, and single-node servers often do, but it does not survive horizontal scaling: a request may land on a node that never issued the nonce, producing spurious 401s. That pushes implementations toward stateless nonces built from a shared secret and a timestamp, which then makes nc high-water-mark tracking the piece that still needs shared state.

saying these in an interview costs you the question

  • Saying the cnonce protects the server — it protects the client against a malicious server
  • Believing nc alone stops replay when the server never checks it
  • Treating `stale=true` as an error to surface to the user
  • Assuming the nonce is a session id or that it authenticates anything by itself
  • Claiming replay protection also protects the request body

context