This post follows from the third part where we implemented CORS.
Up until this point we’ve been sending data back and forth from our API as naked as the day we were born. Anyone who bothers to sniff our traffic can read it, and while that doesn’t really matter much with our albums names, we’re likely to be sending more sensitive information in the future.
That leads us to HTTPS, which is the secure version of the HTTP protocol. Here the S means secure, which says we’re using TLS, or Transport Layer Security, which is usually used to encrypt Application Layer protocols like HTTP. The Application Layer is the 7th, or top layer of the OSI model, which is a model that describes how data goes on its journey from one system to another. This model was developed by an ISO committee in 1979-1980, and is meant to be a way to think about network communications. I don’t know why it’s 7 layers and not 5 or 12. Lucky perhaps?
In the end it doesn’t matter how many layers it has, just think of things going from an app to your OS to your network adapter and then out to your router as spurts of light and electricity zipping through cables, only to find themselves on the larger internet, lost, alone and afraid. Looking at the diagram above and finding TLS you may wonder what’s a Transport Layer protocol doing all up in our Application Layer? Well, it’s actually not, but it is patiently waiting for the Application Layer to finish doing its business before it takes the data and secures it.
TLS encryption is both asymmetric and symmetric, which means using a pair of public and private keys and a single shared key, respectively. Asymmetric encryption is used to create the initial connection, referred to as the handshake, and symmetric is used with the established session. If you’re not sure how all this cryptography stuff works, meaning the difference between symmetric and asymmetric encryption, then I’d recommend reading a quick introduction.
TLS 1.3 is the current version of the protocol, with TLS 1.0, 1.1 being compromised in some way, and TLS 1.2 still being fine to use. You may be wondering how a cryptographic protocol can be compromised? Isn’t there some mathematical basis which makes all this stuff secure? Did someone make a mistake along the way?
It turns out that certain hashing algorithms, such as SHA-1, are vulnerable. By vulnerable it means that people were able to find hash collisions, or situations where two inputs yield the same hash. How that was done is a bit beyond the scope of this article, but it’s my understanding that it came about due primarily as a result of the massive increase in computational speeds we’ve had in the last 30 years.
Cryptographic hash functions are supposed to yield different hashes for different inputs, which makes them useful as digital signatures and for verifying stored data hasn’t been tampered with. Passwords, for example, are hashed when they are stored in a database. When you log into a site the password is hashed and then compared to the stored hash value. If it’s the same, you’re allowed to login, and if not, then you can’t.
A good hashing algorithm should be 1-to-1, meaning for every unique input you get a unique output. If you can have two inputs that yield that same output (a collision) that makes proving you logged in with the correct password impossible. This post does a good job of explaining why it’s a problem if someone can reliably produce the same hash from different inputs.
All this relates to TLS, since early versions of the protocol used SHA-1, and other later compromised hash functions, in the cipher suite. A cipher suite is the collection of algorithms the protocol makes use of. If you scroll down on this page to the section on Key exchange or key agreement you can find a list of algorithms used (aka the cipher suite) and which version of both SSL and TLS used them. It seems a number have been removed with TLS 1.3.
Despite the comprised version of TLS, and unlike the typical MMO release, which needs to be updated every 24 hours in the months after launch, TLS has only had four versions in the last 20+ years. As it turns out the majority of cryptography related vulnerabilities are actually from the software, or processes which make use of it, as opposed to the algorithms in the cipher suite. These vulnerabilities can relate to the way the operating system is implementing encryption, or with the software that uses the encryption method. Instead of going after the algorithms themselves, people poke holes in the software which uses them.
All this is a really long way of saying, we need to secure our API with HTTPS/TLS. We’re going to do that locally using mkcert, which you should install now.
You can find the code for this post in the repo under the server/src/bin folder in the file tls-1. The the difference bewteen this and the previous post on CORS is first the opensssl imports:
|
|
And then we added the following to main:
|
|
Let’s run it and see what happens:
$ cargo run --bin tls-1
It should complain that it can’t find localhost-key.pem, which is required as according to the second line below:
|
|
Let’s look at what this chunk of code is doing, because there’s a lot to unpack here. First, SslAcceptor is a type that wraps a stream in a TLS session. On the surface that means it’s setting up a connection for us, but if you look into the source you’ll find it’s setting Diffie-Hellman parameters as well as deciding which ciphers it will allow. It does this because, as it says right at the top of the docs:
OpenSSL’s default configuration is highly insecure. This connector manages the OpenSSL structures, configuring cipher suites, session options, and more.
Well that’s interesting. So that means this type will need updating anytime one of the ciphers becomes compromised. I wonder what other setups are like this and how vulnerable that makes particular systems where these things are hardcoded in?
Looking back at our three lines of code, and then the last line, it mentions a certificate chain file. That opens up another question of what a certificate is and why there’s a chain. We’ll discuss that in more detail once we get the program up and running, but for now know that a chainfile (aka certificate chain) is a list of certificates where each is signed by the previous one, often going back to a Root CA (Certificate Authority). But, as I said, we’ll dive deeper in a moment. For now, let’s get our server running.
To do that we need to create these .pem files using mkcert by first installing a local certificate authority:
$ mkcert -install
You should see something like:
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
Keep in mind that you’ve just install a trusted key for your system. The warning from the mkcert repo is clear on this:
Warning: the rootCA-key.pem file that mkcert automatically generates gives complete power to intercept secure requests from your machine. Do not share it.
Because of that, it’s probably best to uninstall the key when you no longer need it by using:
$ mkcert -uninstall
With that warning out of the way, let’s install your keys:
$ mkcert localhost
And now you’re done, so you can rerun your program and it should work.
Now, let’s backtrack and look at what we’re talking about. When we ran mkcert in created a new local CA, where CA stands for certification authority. A certificate authority is what helps us establish an HTTPS connection by issuing a digital certificate that says “yes, this site owns this public key.” Essentially it’s verifying a site’s identity.
Anyone can create their own certificate authority, but only some are recognized and trusted. Your browser actually has a listing of these. If you look in your settings under Manage certificates in a Chromium-based browser, you’ll find a list of them:
Why are these trusted? Because the browser manufacturers say they are. That’s really it. I’m also pretty sure there’s someone somewhere making a bunch of money off this whole thing.
What happens when you vist a site over HTTPS that site is the site will send you their public key and a digital certificate signed with the private key of a trusted CA. That certificate should be listed in your browser’s listing, as shown above, along with the public key of that CA. You can then use the public key to open the certificate and check that the IP address inside matches that of the site you’re visiting.
If the IP matches, your browser will use that site’s public key to
The contents of the certificate will contain the IP of the server you’re visting. At that point, your browser will generate a symmemtric key which it encrypts using the public key from the server and then sends it back. Now you and the server use the symmetric key to form a secure connection. When the session is dropped the key is disposed of.
And that wraps up a load of info for just three new lines to your program, but before we go on to JWTs I want to say that using mkcert is really just a way to do this locally. If you want to use HTTPs for your web server you should probably use something like Let’s Encrypt.