After explaining all the ways that trust can be subverted by DNS, CA, and a random wave to wipe away the writing in the sand, let us get down to the actual details about what matters here.
HTTPS/SSL/TLS, whatever it is called this week, provides confidentially over the wire for the messages you are sending. What it doesn’t provide you is confidentially from knowing who you talked too. This may seem unobvious, at first, because the entire communication is encrypted, so how can a 3rd-party know who I’m talking about?
Well, there are two main ways. It can happen through a DNS query. If you need to go to “http://my-awesome-service,” you need to know what the IP of that is, and, for that, you need to do a DNS query. There are DNS systems that are encrypted, but they aren’t widely deployed and, in general, you can assume that people can listen to your DNS and figure out what you are doing. If you go to “that-bad-place,” it is probably visible in someone’s logs somewhere.
But the other way that someone can know who you are talking to is that you told them so. How did you do that?
Well, let’s consider one of the primary reasons we have HTTPS. A user has to validate that the hostname they used matched the hostname on the certificate. That seems pretty reasonable, right? But that single requirement pretty much invalidates the notion of confidentiality of who I’m talking to.
Consider the following steps:
- I go to “https://my-awesome-service.”
- This is resolved to IP address 184.108.40.206
- I’m starting an SSL connection to that IP, at port 443. Initially, of course, the connection is not encrypted, but I’ve just initiated the SSL connection.
At that point, any outside observer that can listen to the raw network traffic knows what site you have visited. But how can this be? Well, at this point, the server needs to return a reply, and it needs to do that using a certificate.
Let us go with the “secure” option and say that we are simply sending over the wire an “open SSL connection to 220.127.116.11.” What does this tell the listener? Well, since at this point the server doesn’t know what the client wants, it must reply with a certificate. That certificate must be the same for all such connections and the user will abort the connection if the certificate does not match the expected hostname.
What are the implications there? Well, even assuming that I don’t have a database of matching IP addresses to their hostnames (which I would most assuredly do), I can just connect myself to the remote server and get the certificate. At this point, I can just inspect the hostname from the certificate and know what site the user wanted to visit. This is somewhat mitigated by the fact that a certificate may contain multiple hostnames or even wildcards, but even that match gives me quite a lot of information about who you are talking to.
However, not sending who I want to talk to over the initial connection has a huge cost associated with it. If the server doesn’t know who you want, this means that each IP address may serve only a single hostname (otherwise, we may reply with the wrong certificate). Indeed, one of the reasons HTTPS was expensive was this tying of a whole IP address for a single hostname. On the other hand, if we sent the hostname we were interested in, the server would be able to host multiple HTTPS websites on the same machine, and select the right certificate at handshake time.
There are two ways to do that, one is called SNI – Server Name Indication. Which is basically a header in the SSL connection handshake that says what the hostname is. The other is ALPN – Application-Level Protocol Negotiation, which allows you to select how you want to talk to the server. This can be very useful if you want to connect to the server as one client with HTTP and on another using HTTP/2.0. That has totally different semantics, so routing based on ALPN can make things much easier.
At this point, the server can make all sorts of interesting decisions with regards to the connection. For example, based on the SNI field, it may forward the connection to another machine, either as the raw SSL stream or by stripping the SSL and sending the unencrypted data to the final destination. The first case, of forwarding the raw SSL stream is the more interesting scenario because we can do that without having the certificate. We just need to inspect the raw stream header and extract the SNI value, at which point we route that to the right location and send the connection on its merry way.