A case against TLS
Introduction
Should we MAC-then-encrypt or encrypt-then-MAC? This one and dozens of other pitfalls in designing a cryptographic protocol has lead to the general consensus that cryptographic protocols shouldn’t be developed ad hoc but cryptographic libraries that have been carefully reviewed and scrutinized should be used. Such a library transparently takes care of the pitfall above. It would implement the cryptographic handshake and key exchange, know how to talk to an older protocol version, make sure a nonce is used only once, handle reply attacks, implement perfect forward secrecy, etc.
The standard choice is a TLS library, most often OpenSSL. TLS is wide-spread and “well understood”. But I want to argue that if you’re not talking to a browser then you should really consider using something else than TLS.
Complex certificate handling
It doesn’t matter which TLS implementation you choose you will likely
end up using the openssl tool to create your certificates. The tool’s user
experience isn’t intuitive and it’s hard to memorize the
relevant commands.
Moreover, you will deal with *.csr, *.pem, *.key, *.pkcs12, *.pfx, *.p12, *.der, *.cert, *.cer, *.crt, *.p7b, *.keystore, *.crl, files.
I think the amount of different files (or extension aliases) is a very good
proxy for the underlying complexity which impacts security and the deployment
process.
Obscure APIs
The most popular TLS implementation is OpenSSL. OpenSSL is known to have APIs that are hard to use correctly. So even if OpenSSL has no implementation errors the software using it can potentially degrade security by incorrect use of those APIs. BoringSSL and LibreSSL being OpenSSL forks will have a similar issues. Other TLS implementations won’t reduce the complexity of their APIs much because there’s also complexity that’s inherent to TLS. Go’s TLS package has the cleanest API I have seen out there. I don’t expect any more room for complexity reduction.
As long as crypto APIs are obscure they will be used incorrectly. Dan Bernstein is the most prominent advocate for crypto APIs that are easy to use correctly. NaCl’s API for example only returns the message after its signature has been successfully verified. This eliminates the possibility to forget - or accidentally remove - a signature check.
Google’s Building Secure and Reliable Systems has an entire section called “Simplicity Leads to Secure and Reliable Code”. I couldn’t agree more and that is why the point I’ve raised in this section is the most critical one.
TLS is for browsers
TLS - formerly SSL - was designed for browsers by Taher Elgamal while working at Netscape. The goal was to introduce secure channels for confidential data on the internet such as credit card data. That facilitated “e-commerce” as it used to be called during those days.
Consequently, TLS implements a lot of features that are useful to browsers but not so much in situations when services are talking to each other.
Backwards compatibility
TLS can always talk to older TLS protocol versions because not all browsers are being kept up to date - be it in corporate environments or on unmaintained home PCs.
This backwards compatibility is not useful if both communication endpoints are controlled by you. On the contrary, TLS has a history of doing it wrong and was subjected to downgrade attacks. Supposedly, TLS 1.3 has finally fixed it.
Authentication through certification authorities
It’s impossible to predict what websites a user will visit with his browser. Therefore, websites need to be authenticated on demand. The solution in the TLS world is to use certification authorities for that.
But you will know upfront which services your service will communicate with. You will configure their hostname/IP address and you can also configure their certificates/public keys at the same time.
Certification authorities have a reputation of not doing their job diligent enough. There’s a long list of incidents where browser and OS vendors have had to distrust certificates or even a certificate authority’s root certificate.
Attack surface reduction
Even if you settle for TLS 1.3 (you really should) the TLS library will still implement previous TLS versions for other use cases. This unnecessarily widens the attack surface. It is common that software is attacked through unused features.
History of vulnerabilities
OpenSSL, the most popular implementation, is infamous for its long and ongoing history of vulnerabilities. Mozilla’s NSS accounts for mutliple vulnerabilities every year; same for wolfSSL, GnuTLS, JSSE, etc.
But TLS 1.3 is great!
TLS 1.3 is a huge step forward. It retired an array of legacy cryptographic primitives, fixed the downgrade attack issues and optimized the handshake. Still, I think that the points raised here remain valid.
But are there alternatives?
Yes, there are! From top of my head I can name the following:
- Dan Bernstein’s NaCl
- Google’s tink
- Noise Protocol Framework
- SSH
SSH, seriously?
Kind of! I’ve chosen it just to prove my point. Almost every developer out there has already configured a CI/CD service to access other services such as a Git repository. You probably too. How did you like the experience of “creating the SSH certificate” (a.k.a. key generation) and deploying it on the CI/CD service? Is it preferable to the experience you have had with TLS certificates? How confident do you feel when dealing with SSH compared to TLS?
With that said, SSH is also no silver bullet. Its process of obsoleting dated cryptographic primitives is conservative. There are article’s on the internet with instructions on how to configure OpenSSH to only use strong and modern cryptographic primitives. But I guess this is a topic for another blog post.