Summary

  • RFC 9729 lets a provisioned client authenticate on its first request by signing TLS-exporter-derived material; the origin need not expose a 401 challenge that confirms a protected capability exists.
  • The mechanism transfers control into external key distribution, the exact TLS session and terminator, a trusted frontend-to-backend exporter assertion, response/timing uniformity, connection-scoped replay controls and revocation evidence.
  • “Looks like 404” is a public privacy behavior, not an operational diagnosis. A sound deployment keeps the outside response uniform while retaining access-controlled proof of why the request failed and which control generation decided it.

A stranger requests /private/operations. The server returns the same page, status, length and timing profile it would return for a path that was never configured. An authorized client sends the same request on a suitable TLS connection, but includes a Concealed credential and receives the service. The difference is not a secret URL alone. It is a proof assembled before the server says authentication is available.

That is the practical change in RFC 9729. Conventional signature schemes often need a fresh server challenge. The challenge confirms that something behind the path expects credentials. Concealed authentication uses a TLS keying-material exporter for freshness instead, allowing the credential to arrive unprompted. The challenge oracle disappears. The authority needed to make the first request valid does not.

The credential starts outside HTTP

The RFC assumes an external mechanism distributes keys. A client has a key identifier and an asymmetric public/private key pair. The origin maintains a mapping from accepted identifiers to public keys. The protocol does not decide who qualifies, who may issue a key, how a private key is protected, how a lost credential is reported, or when a former employee, device or community member stops being eligible.

Those are not implementation details beneath the security story. They are the first control plane. A perfect signature against a stale acceptance row is perfect proof of the wrong policy state. Deleting a client file is not revocation if a gateway or backend still accepts the public key. Removing a database row is not proven revocation until every verifier that can answer the resource has loaded the new generation.

The useful inventory therefore describes relationships, not merely keys: issuer, subject class, permitted origin, realm if used, public-key fingerprint, activation time, expiry, revocation state, database generation and every verifier expected to consume it. The private key remains private; the control evidence does not need to be.

What the TLS exporter binds

The client invokes the registered exporter label EXPORTER-HTTP-Concealed-Authentication. Its context contains the TLS signature-scheme number, key identifier, public key, URI scheme, host, port and optional authentication realm. The exporter returns 48 bytes. Thirty-two become part of the asymmetric signature input; sixteen travel as the v verification parameter.

The HTTP credential also carries k for key ID, a for public key, p for proof and s for signature scheme. The explicit public key is checked against the stored key to resist key confusion. The verification bytes make the server check the exporter result as well as a signature that some unusual key might validate for more than one input.

This context is deliberately narrower than an HTTP request. It binds an origin-shaped tuple and a TLS session, not a method, path or body. On one connection, the same key produces identical authentication proofs for multiple requests. That improves compression, but it means a context that can read another context’s Authorization header may replay it on that connection. Multiplexing isolation is part of the authentication boundary.

It also means “fresh” needs a timestamp. The proof is as fresh as the underlying connection. A long-lived or resumed connection may keep a valid exporter context longer than an operator’s intuition about a fresh login. A server that needs a tighter window can force a new connection, but that choice has capacity, latency and failure consequences. Connection age, resumption lineage and forced-turnover reason belong beside the authentication verdict.

The TLS terminator becomes an authentication principal

RFC 9729 separates the server into a frontend that terminates TLS or QUIC and a backend that holds the accepted-key database. In a single process, the exporter bytes can cross a function boundary. In a split deployment, the frontend sends the original Authorization field and a new Concealed-Auth-Export field containing the 48-byte exporter output.

The backend cannot reproduce client-to-frontend exporter material from its own connection. It must trust the frontend’s assertion. The RFC makes the resulting rule explicit: a backend ignores that field unless it already trusts the sender; a frontend must not forward a copy supplied by the client.

This is where a load balancer stops being “just transport.” It can manufacture the channel-binding input that the application uses to accept an identity. A backend network ACL alone may be too broad if several proxies, sidecars or tenants share the path. The evidence needs the authenticated frontend identity, software and configuration generation, client-facing connection identifier, selected virtual host, normalized authority, exporter fingerprint or protected correlation value, and backend route.

If a service mesh strips the original credential, duplicates the exporter field, changes the host used in context construction, or routes the request after verification to another authority domain, the public client may see only 404. Concealment has not contained the fault. It has contained the symptom.

Five checks, then a separate authorization decision

The backend parses every required parameter, finds the key ID, compares the stored public key with the presented one, compares the verification bytes with the exporter output, and verifies the signature. Any failure is treated as though the credential were missing.

Passing all five checks allows the backend to consider the request authenticated. It does not state that the requested operation is authorized. A credential may identify a contractor who can read one capability but not another. A key may be valid while an account is suspended. A correct request can be routed to a service whose policy generation is old. The evidence chain must retain authentication result, authorization policy and resource effect as separate decisions.

That separation also prevents a misleading success metric. “Signature valid” can remain green while every authorized client receives the nonexistent-resource response because the entitlement table failed to load. Conversely, a routing rule can expose a resource without executing Concealed verification at all. The service result has to be observed from the client side and joined to the verifier’s internal record.

Uniform failure is a designed disclosure policy

For a resource that is meant to be non-probeable, the RFC requires an authentication failure to receive exactly the response used for a nonexistent resource. That can mean 404 rather than 401. Status is only the first coordinate. Body, headers, cache behavior, connection handling, content length and latency can all distinguish branches.

Cryptographic verification costs time. A nonexistent path that returns immediately while a protected path performs a key lookup and signature check becomes a timing oracle. Adding delay to selected nonexistent paths can narrow the gap, yet a distinctive delay distribution may reveal that the origin uses concealment at all. The correct test compares populations across network conditions and load, not two stopwatch samples.

Discovery can also occur elsewhere. An unauthenticated index, link, sitemap, error message, analytics event, client bundle, documentation page or DNS name can disclose the path. Concealed authentication removes one protocol signal. It cannot revoke every other publication decision.

The standard itself needs versioned test evidence

The RFC 9729 errata record already contains an important implementation lesson. Verified erratum 8807 corrects the hexadecimal example for the signature context: the published figure encoded HTTP Signature Authentication, while the normative construction says HTTP Concealed Authentication. Copying the example instead of testing the named context can create interoperability failure hidden behind an intentionally generic response.

A second, still-reported erratum concerns the integer ABNF for s. The printed expression excludes one-digit nonzero values even though the prose allows 0 through 65535. Operators should track the erratum’s status and test the parser they actually deploy. Silently choosing a preferred reading and then reporting “bad credentials” makes standards ambiguity look like user error.

The surrounding registries remain useful and limited. IANA records the Concealed authentication scheme, the exporter label and the Concealed-Auth-Export HTTP field. Registration proves shared names and references. It does not prove client support, correct deployment, key issuance, route coverage or response uniformity.

A two-surface evidence model

The deployment needs two deliberately different outputs. The public surface minimizes distinctions available to an unauthorized requester. The private surface, accessible only to accountable operators, preserves structured distinctions: malformed credential, unknown key generation, revoked key, public-key mismatch, exporter mismatch, invalid signature, ineligible TLS version, untrusted frontend, authorization denial, wrong route and downstream failure.

Those reason codes must not be reflected into public bodies or timings. They should be joined through a protected correlation identifier with connection and configuration facts, retained for a stated period, and audited for access. Privacy outside does not justify a universal debug log; it justifies precise evidence with narrower readership.

The most important negative control is ordinary nonexistence. A test set should contain unconfigured paths, concealed paths with no credential, malformed credentials, unknown and revoked keys, valid keys on the wrong host or port, valid credentials on an ineligible TLS connection, client-injected exporter fields, trusted and untrusted frontends, and valid authentication followed by authorization denial. Public observations should converge where policy requires. Internal diagnoses should remain different.

Sources