Summary

  • RFC 2069 replaced Basic’s cleartext password transmission with a challenge response over H(A1), a realm-scoped verifier that still had to be protected as though it contained the password.
  • Its basic response bound knowledge of that verifier to a server nonce, HTTP method and request URI; it did not encrypt content, cover every message field, prove client intent or certify an application outcome.
  • A timestamped, address-bound nonce could limit a replay window without server-side transaction state. Strict one-use evidence required the server to remember used responses until expiry, turning replay resistance into an operating-cost choice.

The improvement was real and deliberately narrow

RFC 1945 documented HTTP/1.0 Basic authentication in 1996. Its credentials were a user identifier and password joined by a colon and represented with Base64. Representation was not encryption. Anyone able to observe that header could recover a reusable password.

RFC 2069, published alongside the first HTTP/1.1 specification in January 1997, attacked that precise weakness. A server sent a challenge containing a nonce. The client returned a digest computed from a shared password, the challenge and selected request fields. The password did not cross the network in cleartext.

The document did not advertise a complete security system. It called Digest a weak access-authentication method, said it encrypted no object content, left initial password establishment outside its scope, and compared it unfavourably with Kerberos and client private-key schemes. Its value was relative and concrete: passive observation of one exchange no longer yielded the password itself.

That historical modesty matters. “Better than Basic” is an engineering claim about one failure mode. It is not evidence of confidentiality for the message, strong identity for the server, immunity from password guessing, or safe operation for every method. The accompanying RFC 2068 supplied the HTTP/1.1 setting; neither publication proves which servers implemented the scheme or how they configured it.

The formula names the proof surface

For the only concrete algorithm specified in RFC 2069, H(data) meant the MD5 function defined by RFC 1321. The response was built in three steps:

A1 = username : realm : password
A2 = Method : request-URI
response = KD(H(A1), nonce : H(A2))

The colons and exact strings mattered. The realm was inside A1; the method and URI were inside A2; the server’s nonce joined the final computation. A matching response therefore connected four statements: the submitted username belonged to this realm, the sender possessed material equivalent to the password for that realm, the response was made for this challenge, and it named this method and URI.

That is a useful proof. It is also a finite one. Ordinary headers not named by the formula were not automatically protected. The TCP path was not authenticated. The basic response did not encrypt the request or response body. It did not say why the client sent the request, whether a person approved it, whether the account was authorized for the resulting action, or whether an application committed that action once.

RFC 2069 even required the authenticating server to check that the uri directive designated the document actually served. The duplicate URI existed because a proxy might alter the request line. A valid arithmetic result was insufficient if the verifier attached it to the wrong resource.

For POST and PUT, the specification offered a separate optional entity digest covering the body and selected entity metadata, and recommended either using stronger one-time treatment or limiting Digest to GET. Optional coverage is an important clue: the base response was not a signature over the whole HTTP message.

The hash file still contained a key

The server did not need the cleartext password to verify a request. It could store the username beside H(A1) and use that value as the secret input to the final computation. This reduced one custody problem but created a different description of the asset.

RFC 2069’s password-storage section is unusually explicit. If the file holding H(A1) values were compromised, an attacker could gain immediate access to documents in that realm without first recovering the password. The file therefore had to be protected as if it contained unencrypted passwords. Brute force might still recover a weak human password, but recovery was not necessary for realm access.

Calling H(A1) a password equivalent must remain scoped. The realm is part of the digest. A verifier copied from one realm does not automatically authenticate in every other realm that happens to use the same username and password. That compartment is why the RFC recommended unique realm strings, including the authenticating host. Yet within its own realm, the stolen value is executable authority, not harmless residue.

This distinction is easy to lose in an inventory. A column labelled “hash” sounds less sensitive than one labelled “password”. Operationally, classification should follow what the value can do. If possession lets software generate an accepted Authorization response, custody controls must protect that capability, not its comforting name.

A nonce was a policy container

RFC 2069 left nonce contents to the implementation and recommended a construction resembling:

H(client-IP : timestamp : server-private-key)

The server could recompute such a nonce when a request arrived. Binding the client address limited where it could be reused; binding time limited how long. The private key prevented a client from minting arbitrary valid challenges. This design could operate without remembering every issued nonce between transactions.

But stateless validation answers only “was this challenge minted for this address and is it still within its window?” It does not answer “has this exact authorization already been accepted?” Two identical requests inside the valid interval can both pass the same arithmetic test.

For a simple GET, RFC 2069 judged replay less useful because the URI was bound into the response and an eavesdropper had already observed the returned document. Even there, a GET carrying form data could trigger server action. For POST and PUT, the document warned that replay could submit counterfeit form data or a counterfeit file.

Where no replay could be tolerated, the server could use one-time responses and remember every used digest until the nonce expired. That gives a much stronger answer, but it has a cost: storage, lookup, expiry, collision handling and consistency across every verifier that may accept the request. Replay resistance was not a decorative property of the digest. It was the combined result of nonce design and maintained state.

The later standards expose what was missing

RFC 2617 replaced RFC 2069 in 1999. Its RFC Editor record says so directly and notes that some optional RFC 2069 elements were removed after problems were found. The successor introduced the strongly recommended qop path, a client nonce (cnonce) and a nonce count (nc).

With qop, the response included the server nonce, the client’s count, the client nonce and the selected protection mode. A server maintaining its own count could identify the same nc appearing twice. qop=auth-int also placed a body hash into A2. If qop was absent, however, RFC 2617 retained the old RFC 2069 construction for compatibility. The compatibility branch is a precise historical boundary: later counters and client-chosen entropy were repairs, not hidden properties of the 1997 base formula.

RFC 7616, whose metadata record marks the later lineage, added SHA-256 and SHA-512/256 and made MD5 not recommended. It also preserved the warning about H(A1): a stolen verifier still grants immediate access within its realm and must be guarded like a cleartext password. The newer RFC says that algorithm agility does not make human-memorable passwords safe from dictionary attacks and recommends a secure channel such as HTTPS.

Modern HTTP Semantics in RFC 9110 helps keep the final boundary straight. Authentication credentials can inform an authorization decision. They are not themselves the application decision, the transaction commit or the effect observed by a user.

A valid digest was not a receipt for intention

Suppose a log shows a valid response for POST /transfer. It can support a narrow reconstruction: a verifier accepted a calculation made with the relevant secret material, nonce, method and URI. Other evidence is still required.

Was the body covered? Was the nonce unused? Which server checked replay state? Did all nodes share that state? Which principal did the username map to at that time? Was that principal allowed to transfer this resource? Did the application commit once, twice or not at all? Did a person intend the command, or did malware reuse credential material? Digest verification does not answer those questions merely because its hexadecimal value matches.

This is the useful discipline in Heng Lu’s Running-Code Primacy and Minimum Initial Specification: identify the deterministic common rule, then leave later choices and evidence with the systems that actually execute them. The digest formula is locally verifiable. Password-file custody, nonce lifetime, replay state and application authorization are distinct operating decisions.

His note on reality layers supplies a further warning against symbolic substitution. A matching hash is a record at one layer. It cannot become, by rhetoric, proof of every later layer. RFC 2069 is most valuable when read with the same restraint with which it was written: it removed one exposed secret from the wire and made the remaining boundaries visible.

Sources