SSL, TLS & HTTPS - Explained with Hands-On example with OpenSSL
??Prasenjit Sutradhar
Software Engineer (Backend & DevOps) ?? | Java, Spring Boot, Node.js, Kafka, Microservices, AWS ??? | Building Early-Stage Startups & SaaS Products ??
??Encrypted Connections with SSL and TLS
Before we add any security or authentication, one of the things that we'll be doing most often with our web servers is making sure that they use HTTPS. That's the secure version of the HTTP protocol, which encrypts our data. That is, HTTPS encrypts our data in transit as it's being sent over the internet. So far, our servers have only ever handled requests with the HTTP protocol. It's always best to talk to our server and make requests, for example, to some dynamic web page to get some data using the HTTPS protocol,
which will make sure that our data for our request is encrypted while it's being sent over the internet to the server, which keeps our client application secure using HTTPS. Both the requests we send, as well as the responses we get back from the server, are sent in a way that's private to our users
so that if someone tries to come in and steal our data well, they can try however hard they might. But they won't be able to crack that encryption and read the data that we're sending or worse off, modify it somehow.
In other words, HTTPS prevents other users from eavesdropping on our data and also from tampering with it. That's what we want. Let's look at exactly how this happens using the SSL or tell protocols. When we browsed a site using an unencrypted HTTP connection,
the data in our request to the server and the data and the response can be read by anyone with access to our network, who can read the traffic between us and the server using something like what's called a packet analysis tool, something like Wireshark.
Which can potentially inspect all of the traffic that's crossing our network. Wireshark is a little bit beyond the scope of this article. So if you're interested, check out an ethical hacking course. But what we need to know is that Wireshark has the ability to inspect traffic as it's being sent across our network. And it's especially easy if it's not encrypted. That's not great if the traffic that we're sending say in our request includes a password or some other data, we'd rather keep private.
On the other hand, when we browse through a site like Google using an encrypted HTTPS connection, which we can see in our address bar, what happens is that we're now using the encrypted HTTPS protocol that underneath the hood uses the powerful encryption of SSL and TLS.
SSL and TLS are usually used to refer to the same thing. To be fully accurate, TLS is the latest version of this encrypted protocol. So we started off with SSL1.0, SSL2.0, and SSL3.0 and then moved on to TLS1.0 and kept getting new versions from there.
But many people still say SSL when referring to TLS. So it's good to be aware of both terms. HTTPS doesn't change the HTTP protocol. We still have that same old http connection,
but we now take our HTP requests and responses and wrap them in this really strongly encrypted connection provided by TLS. And it's this strong outer shell that becomes, an HTTPS connection.
Only the domain name of the site you're browsing is exposed in what's called plain text because the domain is needed to set up that connection. But all of the data in the body of the request and any specific paths that you're browsing to are all securely encrypted with TLS, say you're browsing Google and running some searches.
Anyone snooping on your connection will know that you're browsing Google.com, but not what you're searching for or what data you're sending and receiving. That's kept secure because Google uses TLS and the HTTPS protocol.
??Digital Certificates, Signing, and Man In The Middle Attacks
We've learned that HTTPS uses the SSL and TLS protocols to encrypt our HTTP connections. Now, for our data to be encrypted, we need what's called an SSL or TLS certificate.
This is a type of digital certificate that's used to verify the server's ownership prior to any encrypted data being sent. Just like on a physical certificate that you might sign if the digital signature of this digital certificate is valid and we trust the person who issued this TLS certificate, then we say, OK, I'm willing to use the key that this server sent to send encrypted data back and forth with that server. This is used to verify that we're actually talking to the server that we expect. It's not enough that the data is encrypted.
If we're sending encrypted data to some malicious hacker who has the key to decrypt that data, that's what's called a man-in-the-middle attack where a hacker pretends to be the server that we're talking to and instead steals that data for their own purposes with digital certificates. We verify the server's ownership first to prevent these men-in-the-middle attacks. Now we mentioned that these certificates are assigned and they're signed by what's known as a certificate authority.
This is an organization that your computer or your browser already trusts to issue valid digital certificates that actually verify the real server's owner and aren't just created by some malicious hacker. Now, in the early days of the Internet, you had to buy these certificates from certificate authorities which would potentially cost hundreds of dollars. But lucky for us, there are now certificate authorities that allow us to sign these certificates for free. One of the most well-known is Let's Encrypt.
They're backed by Google, Facebook, and all kinds of other giant companies to provide you with the same security. With the added cost, however, Let's Encrypt will only give you a certificate if you meet certain conditions. For example, your site needs to have a domain name and can't just be an IP address on some server somewhere. This is why we have a type of certificate called a self-signed certificate that still allows us to encrypt our traffic and use HTTPS,
But we sign it ourselves on our local machine and our local machine. Our certificate isn't trusted by others. We're not registered as a certificate authority and we haven't built up that reputation that these large CAs (certificate authorities) have built up for themselves. Companies like Google and Facebook. So self-sign certs are useful for development because we can sign them for IP addresses like localhost, but they're not useful in production because they're still vulnerable to those men-in-the-middle attacks.
There's no trusted third party, no certificate authority to verify who you're talking to. Because of this flaw, if your browser detects a self-signed certificate, it will give you a warning,
much like this one telling you that the certificate authority is invalid and you'll only be able to browse the site if you explicitly tell your browser that you understand this risk. This is why in production, we always want to use C.A.-signed or certificate authority-signed certificates
because they're signed by these certificate authorities. They're trusted by most clients on the web. And your browser will tell you that the server you're talking to is trusted. Only the server registered with the certificate authority will pass that check. All TLS certificates can be broken down into these two categories, just like the TLS protocol encrypts your traffic.
Additionally, these certificates help to verify the ownership of the server you're talking to. And that's really what you need to know about TLS. In the upcoming section, I’m going to demonstrate to you how to set up an HTTPS server with Node.JS & Express.
???From HTTP to HTTPS: Setting Up
Now we have a clear idea of SSL, TLS & HTTPS. But how to create an HTTPS server in the real world? Let’s see in this section. To demonstrate, I’m going to use Node.JS, and Express to build a simple web server that works on HTTP and then make it to use HTTPS.
So how do we get our node servers to use HTTPS? How do we get them to respond to HTTPS requests and use this powerful encryption in our APIs? Say we have a barebones Express.JS server. Nothing new here. So the file structure is very simple -
Inside the server.js file, we have an express application that's listening on Port 3000 using the listen function on our app object.
There we have two routes -
All right. As it stands, this value likely won't be very secret, but we're responsible, so let's see that our program works and do:
领英推荐
npm start
All right, the server is listening on Port 300. This means if we go to our browser at localhost on Port 3000, we're getting that root route, which is serving our index.html file, there we see:
And if we go to our ‘/secret’ route,
But as you already know, this data and our secret value are being sent over HTTP by default, which means that the connection to our API is not secure.
Anyone on our network listening to our traffic will see that the response to our request here included our secret value of 1394. Let's fix this. Let's make it a lot harder for any nosy hackers or even other software that's listening to our traffic to know the data that we're sending back and forth.
Now that our HTTP server is set up, we can secure the communication between our browser and the server so that when the secret here is sent, it's being sent encrypted. Let’s see in the next section.
???HTTPS With Self Signed Certificates, and Public Key Cryptography
Let's put HTTPS to use in the Node.JS server. There are many ways of using HTTPS with web servers. Many services exist, for example, on Google Cloud or Amazon's AWS. These services, like Amazon's CloudFront,
It allows any server to automatically be served over a content delivery network so your pages get served from the closest locations in the fastest way possible. That's a neat feature, but what we care about is that included in these services is the ability to automatically serve your content using HTTPS.
However, I want us to add HTTPS to our Node.js server code, just to have the knowledge of how it works, but also to understand how to test that our servers work correctly when using the secure HTTPS protocol because this is something that comes up fairly often in our Node projects when we're building sites that pass private customer information like credit card numbers or personal health information, we want to make sure that our servers respond to HTTPS requests correctly, even when we're running on localhost.
Remember how there were two ways of starting our HTTP servers? We could use the listen() function from Express's app object here. Or we could require Node.js’s built-in http module, And then call the same listen function. But this time from Node’s built-in http module like this:
So http.createServer(), which is a function that returns our server, which also has the listen() function. The reason why we sometimes use this longer way of creating our servers is because of the additional flexibility it provides.
For example, just like we have this http module, we also have HTTPS in the built-in https module.
If we now call https.createServer, it also create a server. But the really cool thing we can now do is pass in an option into the createServer method with the object with our SSL certificate, which then encrypts all of the data being sent to and from our server.
So what we do is -
as well as this key, which contains a secret used when we encrypt our data.
We're going to use something called Open SSL to create that certificate.
This is an open-source tool that's available both on Windows, Linux, and Mac systems on Windows machines. If you don't already have it installed, the easiest way to get open SSL set up on your machine is by installing GIT for Windows. If you have GIT for Windows, you should already have an Open SSL set up. And on macOS and Linux, it's quite likely you already have open SSL installed as well. Or you can get it from the openssl.org page right here.
When we have open SSL on our machines, we can go into our terminal and generate a self-signed certificate by running the open SSL command:
and passing in -
So now the result of this command will be two files. The private key and the certificate, this certificate, which is public and which our browser will check to check the ownership of our server. And last but not least,
After that, we need to generate a fresh one if we don't specify the number of days here. It's usually only 30 days. We want it to be long enough that we're not always generating new certs, but also not forever. In case the certificate is stolen or something about our application changes. The certificate is a bit like a driver's license or a passport photo, which you need to update to confirm your identity.
All right. I know this is a long command. Don't worry. Normally, you don't have to memorize all of these options. Let's make that cert by hitting enter:
We're asked for some options, a country name which we can leave blank or we can write our two-letter country code. In my case, it’s IN for India.
We can leave the state or province empty, as well as the next few options. The important one being the common name, which usually when your certificate authority is signing a certificate, they'll set the common name to be the domain name like www.google.com, Which has to match the site that your browser is currently looking at. But for self-signed certificates, we don't necessarily have a domain name. Maybe we're just running on localhost or an IP address so we can just write our own name.
All right, at the bottom, we can leave the email address blank. Now what we have, if I close the terminal, we have two Files - cert.pem & key.pem. Check it out.
This key.pem file contains what appears to be 4096 bits that ensure that were the only ones encrypting data for this server, so ownership of the key means that we're allowed to encrypt data for the server identified by the Cert. And vice versa, the certificates actually allows you to decrypt data encrypted with this key. So that's what the browser would do. It decrypt the data sent from the server encrypted with the private key using the data contained in that certificate, which is why the browser requires access to this public certificate. This approach that I'm describing is what's known as public key cryptography. That's what SSL and TLS use to make sure that your data is kept safe with these two files available.
We can now set the paths when we create a server. So the key is going to be in key.pem and the Cert is going to be inside the cert.pem file. To read data from those files, we need to actually use the built-in file system module, so I'll import that like this -
So what we do with the fs module is we call fs.readFileSync and pass in the paths to our key and certificate to load those files first before passing them in as options to our createServer function.
And that’s all. Now our site is now using HTTPS.
?? Attention: You might still see a “not secured” warning message in Chrome or another browser because we’re using a self-signed certificate issued by you, not by a trusted CA (Certificate Authority). So don’t worry. Underneath the hood, your server is running on HTTPS.