Hi,
Summary
In some situations, ConnectionState.ServerName is not enough and the access to the certificate used to negociate the connection is needed.
This issue targets any stateless protocol that can use SNI and an field (inside the protocol) to identify a virtual host.
In Case of HTTP with TLS , SNI ServerName and the Host header provide valid strings that may differ.
Solution
Adding a ServerCertificate *x509.Certificate attribute to tls.ConnectionState struct
tls.Conn has access to tls.Config so ServerCertificate can be set when (*tls.Conn)ConnectionState() is called.
Illustrations of the problem
the context is a HTTPS offloader/reverse-proxy that have 2 differents certificates each for a different *.FQDN in CommonName ; precisely, cert-1 has *.FQDN-1 CommonName and cert-2 has *.FQDN-2 CommonName
Good case (easily produced with any browser)
- client connects to the server with a SNI header to
foo.FQDN-1 and a Host: foo.FQDN-1
- server sets
request.TLS.ServerName and call ServeHTTP foo.FQDN-1
- inside ServeHTTP,
request.TLS.ServerName and req.Host have the same value
- ServeHTTP answers a redirection to
bar.FQDN-1
- client reuses the same connection & Session Ticket because the Certificate's
CommonName allow it
- inside ServerHTTP ,
request.TLS.ServerName and req.Host have different values because req.TLS.ServerName was set in the first connection
a call to req.TLS.ServerCertificate.VerifyHostname(req.Host) will return nil to confirm that bar.FQDN-1 can be served on the same connection.
Bad case
- client connects to the server with a SNI header to
foo.FQDN-1 and a Host: foo.FQDN-1
- server sets
request.TLS.ServerName and call ServeHTTP foo.FQDN-1
- client crafts a HTTP request to
foo.FQDN-2 and reuse the previous connection
- inside ServerHTTP ,
request.TLS.ServerName and req.Host have different values
Here, a call to req.TLS.ServerCertificate.VerifyHostname(req.Host) will return an error. this call provide an elegant way to mitigate the situation.
There are others way to mitigate this situation but are also more complicated.
Hi,
Summary
In some situations,
ConnectionState.ServerNameis not enough and the access to the certificate used to negociate the connection is needed.This issue targets any stateless protocol that can use SNI and an field (inside the protocol) to identify a virtual host.
In Case of HTTP with TLS , SNI ServerName and the Host header provide valid strings that may differ.
Solution
Adding a
ServerCertificate *x509.Certificateattribute totls.ConnectionStatestructtls.Connhas access totls.ConfigsoServerCertificatecan be set when(*tls.Conn)ConnectionState()is called.Illustrations of the problem
the context is a HTTPS offloader/reverse-proxy that have 2 differents certificates each for a different *.FQDN in CommonName ; precisely, cert-1 has *.FQDN-1 CommonName and cert-2 has *.FQDN-2 CommonName
Good case (easily produced with any browser)
foo.FQDN-1and a Host:foo.FQDN-1request.TLS.ServerNameand call ServeHTTPfoo.FQDN-1request.TLS.ServerNameandreq.Hosthave the same valuebar.FQDN-1CommonNameallow itrequest.TLS.ServerNameandreq.Hosthave different values becausereq.TLS.ServerNamewas set in the first connectiona call to
req.TLS.ServerCertificate.VerifyHostname(req.Host)will returnnilto confirm thatbar.FQDN-1can be served on the same connection.Bad case
foo.FQDN-1and a Host:foo.FQDN-1request.TLS.ServerNameand call ServeHTTPfoo.FQDN-1foo.FQDN-2and reuse the previous connectionrequest.TLS.ServerNameandreq.Hosthave different valuesHere, a call to
req.TLS.ServerCertificate.VerifyHostname(req.Host)will return an error. this call provide an elegant way to mitigate the situation.There are others way to mitigate this situation but are also more complicated.