Golang HTTPS Hello Exchange Hook
I was asked recently to log all client cipher suite capabilities as well as the User-Agent. I mean, come on, how cool to be able to track TLS negotiation capabilities and facet on User-Agent. This type of data would give a lot of insight into how our customers connect to us with TLS. In this blog I attempt to explain my thought process in coming up with a solution to this problem.
What does the STDLIB afford?
The first stop to figuring this out for me was to look at the standard library net/http package for
clues on what data we could get for the HTTPS connection handshake. Looking at the Server there is
an interesting attribute called ConnState
which is a function that takes a net.Conn
implementation, as well
as a http.ConnState
which can be NEW, ACTIVE, etc. This function is called before the http.Server calls the
request handlers by the Server.Serve
method.
Armed with this I created a ConnState Hook function and passed it into the initialization of my server:
By type casting the parameter net.Conn
to a tls.Conn
type we are then able to get at the methods of the tls.Conn
such as ConnectionState
. ConnectionState provides access to various state parameters such as the
negotiated TLS version the client and server have come to. This is pretty interesting, but not exactly the data we want,
we actually want to see what the client is capable of negotiating to…
Looking a bit deeper into the crypto.tls, specifically in tls.Config, I was able to find ANOTHER awesome
callback hook called GetCertificate
which takes a *ClientHelloInfo
and returns a *Certificate
and error. Looking at the
comments:
This will work great for our purposes, and is a pretty neat callback. Basically we would be able to get a custom certificate based on the
Client’s Hello handshake. BUT if we instead return a nil pointer for *Certificate
the tls library will go about figuring out the correct
Certificate itself, which effectively provides us a free hook into the handshake ClientHelloInfo.
Perfect. Updating our code:
In the above code we are ranging over the CipherSuites
and adding to a list of supported Suites for our log message. While
we have access, we are also grabbing the list of Supported Curves and Points for the TLS handshake. The Gist is Here
As you can probably see, there isn’t really a way of linking the ClientHelloInfo with the http.Request in the standard library. As it isn’t a “normal” thing to have access to. I had to change the TLS stdlib to accomodate the request, which isnt great, and I am not happy with it, but it works for the time being.