Summary

  • TLS Server Name Indication is a client-supplied DNS name used early in the handshake to narrow a server's TLS context, certificate or passthrough route. Selection is not proof that the client controls that name.
  • Service-identity verification, HTTP Host or :authority, upstream SNI, ECH inner/outer processing and application authorization are separate decisions. A repeated string does not merge their authority.

The right certificate, the wrong principal

The trace looked persuasive. The ClientHello contained tenant-a.example. The server-name callback selected Tenant A's SSL_CTX. The certificate fingerprint matched the expected deployment. A filter-chain label reported tenant_a.

None of those facts answered the question the gateway needed to ask: who is this client, and may it perform this action?

SNI is sent by the client so that a server sharing an address among virtual services can choose an appropriate configuration before it sends a certificate. Any client capable of constructing a ClientHello can assert any syntactically permitted host name. Selecting the matching certificate authenticates the server to a verifying client; it does not authenticate the client to the server.

The defect was therefore not cryptographic breakage. It was an authority promotion. A routing hint had been renamed as a tenant identifier, and a configuration choice had been promoted into permission.

What server_name actually carries

RFC 6066 defines server_name as a list in ClientHello. The standardized host_name form contains a DNS host name in ASCII form; literal IPv4 and IPv6 addresses are not permitted. A list cannot contain two entries of the same name type.

Those syntax rules improve interoperability. They do not add a credential. The value does not demonstrate a DNS lookup, control of a zone, possession of a private key, membership in a tenant, or entitlement to a resource. A diagnostic client can connect directly to an address and send a different host name. A scanner can enumerate names. A malicious client can choose the most interesting configured value.

The server may use the hint to select a certificate or other security parameters. If it understands the extension but rejects the name, it may terminate with fatal unrecognized_name. It may also continue under a defined fallback. That is why an audit needs the actual absent, unknown and default-host behavior. “We configured SNI” does not say which path ran.

Transcript authentication does not create ownership

TLS 1.3 ultimately authenticates the handshake transcript through Finished. Because ClientHello is part of that transcript, a successful handshake prevents an intermediary from silently changing the ordinary SNI value without detection by the endpoints.

That property is important but narrower than it sounds. It proves that the endpoint saw the same client-supplied value inside this authenticated handshake. It does not prove that the client was entitled to say it. Integrity of a claim is not authority for the claim.

The distinction is clearest when no client authentication is configured. The server can complete a perfectly sound TLS 1.3 handshake, yet know nothing about the client beyond network and application evidence. Server certificate authentication points in the other direction: the client verifies the service.

A control should therefore record client_requested_server_name, not verified_tenant, owned_domain or authorized_origin. Accurate naming prevents a later system from inheriting authority the protocol never supplied.

Certificate selection and certificate validation are different acts

SNI often helps a server choose among certificates. It does not tell the client whether the chosen certificate is acceptable.

RFC 9525 requires the client to construct its acceptable reference identifiers independently, from the source domain and application rules, and match them against presented identifiers in the certificate. Chain validation, validity periods, revocation policy and service-identity matching belong to that verification ledger.

OpenSSL makes the separation operationally visible. A client can set SNI with SSL_set_tlsext_host_name(), but current documentation tells applications to set the expected DNS name for certificate validation separately. A program that sets only SNI can help the server return the intended certificate while still failing to verify that certificate correctly.

Shared certificates sharpen the boundary. One certificate may validly cover several DNS names. That can make two handshakes equally valid cryptographically; it does not merge the tenants or authorize one origin's application request against another.

HTTP authority arrives later

After TLS termination, HTTP carries its own target authority. HTTP/1.1 uses Host; HTTP/2 and HTTP/3 commonly use :authority. RFC 9110 treats that authority as critical request-routing information and warns that it is a frequent attack surface.

SNI and HTTP authority often contain the same name because a well-behaved client derived both from one target URI. “Often equal” is a convenience, not a protocol proof. They are transmitted at different layers, processed at different times and can diverge through testing, connection reuse, proxy behavior, misconfiguration or attack.

The application needs an explicit mismatch rule. A strict virtual host may reject the request. A controlled shared service may permit a documented set. HTTP/2 can return 421 when a connection is not appropriate for the requested origin. What is unsafe is allowing the first name to silently authorize every later request on the connection.

The tenant incident crossed exactly that line: it used the pre-request TLS selector as the application principal. Checking the HTTP name alone would still not have authenticated the client, but it would at least have kept request routing and TLS selection in their correct ledgers.

A proxy has more than one SNI

A TLS-terminating proxy ends the downstream handshake and begins a separate upstream handshake. Each leg has its own ClientHello, server name, certificate, reference identity, transcript and failure state.

The proxy may use a fixed upstream SNI, derive it from the chosen upstream host, derive it from downstream HTTP authority, or apply a controlled mapping. Envoy exposes these as distinct options. NGINX likewise requires an explicit policy for sending SNI to a proxied HTTPS server. Equality with downstream SNI is one possible result, not inherited truth.

The certificate-validation source is another decision. Envoy distinguishes validating a SAN against the actual upstream SNI from validating against downstream request authority. That distinction exists because routing and verification inputs can differ. A dashboard that stores only sni=example destroys the evidence needed to audit which connection, which source and which validator used it.

TLS passthrough creates a different boundary. NGINX ssl_preread can extract SNI from ClientHello and choose an upstream without terminating TLS. The router has evidence that it parsed a hint and selected a destination. It has no evidence that the later handshake completed or that the client accepted the server's certificate.

ECH creates an inner and an outer view

Encrypted Client Hello makes casual SNI logging still less reliable. In the standardized design, ClientHelloInner carries the sensitive server parameters. ClientHelloOuter normally carries the ECH public name needed by the client-facing server and retry machinery.

When ECH is accepted, TLS parameter processing proceeds from the authenticated inner ClientHello. When ECH is rejected, the client may authenticate a connection to the public name only to receive retry information. RFC 9849 explicitly says that authenticating that public-name connection does not authenticate the origin and must not be reported to the application as success.

The outer name is thus useful and intentionally limited. It can take traffic to the client-facing service while concealing the real origin inside. Treating it as the application origin would undo the model's authority boundary even if no ciphertext were broken.

ECH-aware evidence needs at least ech_status, observation role and name source. Logging only “SNI” can mix a public outer name, an accepted inner name and an ordinary plaintext ClientHello. Broadly exporting the inner name also creates the privacy leak ECH was designed to reduce. Observability must prove decisions without rebuilding a plaintext destination archive.

Callbacks are capabilities, not witnesses by themselves

OpenSSL can invoke an early ClientHello callback before ordinary server-name processing, then a server-name callback, then ALPN selection. The server-name callback can switch the connection to another SSL_CTX. GnuTLS similarly lets a client set a name and a server retrieve it.

Those APIs show what software can do. They do not prove which callback executed, which normalized name it saw, which context it selected, whether a fallback was taken or whether a later layer overrode the result. Even timing differs across protocol versions and resumption paths; OpenSSL documents that TLS 1.3 negotiates the server name on every handshake while older-version resumption behaves differently.

The running trace should preserve the input class, selected context identifier, decision reason and terminal state. After a library or proxy upgrade, negative tests must prove those fields still correspond to the actual state machine.

A test suite for denied authority

Begin with the attack the design should make boring. Connect without any client credential, assert a high-value tenant SNI and complete TLS. The correct certificate may be selected, but the application principal must remain anonymous and the privileged route must be denied.

Then remove SNI, send an unknown name and exercise the intended fallback. Send SNI for one tenant and HTTP authority for another. Repeat with a certificate whose SANs cover both names so certificate validity cannot accidentally mask the authorization bug.

Terminate at a proxy and deliberately configure a different upstream SNI. Confirm that downstream selection, upstream selection and upstream SAN validation have separate trace identities. Run a passthrough route and prove it never emits certificate-verified or handshake-complete claims.

Finally exercise ECH acceptance, rejection and ordinary no-ECH operation. Require the trace to say which ClientHello supplied the processed name without leaking the inner name to unauthorized telemetry. A configuration diff is the start of this test, not its result.