It's been long known that TLS is not the best privacy protecting protocol in that SNI leaks what domain the client connects to. I'm a bit surprised that I haven't seen the failure to protect user information when using client authentication mentioned, but it's likely that TLS client authentication is so rarely used, that this have not been on anyone's radar.
TL;DR: Just don't use TLS client authentication on anything before TLS 1.3.
With TLS 1.2 and earlier, if you use client authentication, the client certificate is transmitted in the clear. This contains enough information to uniquely identify the user. If it didn't, then there would be no way for the server to do the authentication.
The danger of this is that Eve (eavesdroppers) on path will be able to track your user's (or your)
connections, where they connect from, figure out how much data they
transfer between to/from your site and likely profile their usage.
I was confident that this was the case as I know that the entire handshake is in the clear. It isn't till the Finished messages that the session becomes encrypted. (TLS 1.3 fixed this by using a new derived key, [sender]_handshake_traffic_secret, to encrypt all the server params, which the client will use to encrypt it's response to the certificate request in the server params.) I decided to verify that this was the case.
I generated a server and a client certificate and key:
openssl req -batch -new -newkey rsa:1024 -days 365 -nodes -x509 -keyout server.key -out server.crt
openssl req -batch -new -newkey rsa:1024 -days 365 -nodes -x509 -keyout client.key -out client.crt
I then launched the server, and included the -Verify and -CAfile options for s_server to request a client certificate:
openssl s_server -accept 5829 -cert server.crt -key server.key -Verify 5 -CAfile client.crt -debug
Then I ran tcpdump to capture the session:
sudo tcpdump -s 0 -n -i lo0 -w clientcert.tcpdump port 5829
And then the client to connect to the server:
openssl s_client -connect localhost:5829 -key client.key -cert client.crt -debug
A usual, non-client authenticated connection and close was about 17 packets, but when I included the client authentication, it became 42 packets (the answer!).
I loaded the packet capture into wireshark, applied the SSL protocol analysis and confirmed that the client certificate was present in clear text:
So, there you have it. Do not use client authentication, pre-TLS 1.3, if you care about the privacy of your users.
It is safe to use client authentication w/ a TLS 1.3 server as long as the server requires all clients be 1.3 clients. If the key exchange algorithm is one of DHE_DSA, DHE_RSA, or an ECDH key exchange algorithm, the random bytes in the Hello messages are signed and these bytes are used by TLS 1.3 for downgrade protection. As the signature covers these bytes, the client would be able to detect any attempts to modify the server or client handshake messages to force a downgrade before it would send the client certificate.
Thanks to Mike Hamburg for reviewing an earlier version of this blog post and pointing out that TLS 1.3 was not vulnerable to this and helping w/ some of the research to prove it.