How to deploy modern TLS in 2019?

By João Poupino

Exciting things have been happening on TLS land lately. TLS 1.3 was finally released, bringing many security and performance improvements. It would be awesome if everyone switched to TLS 1.3 right now, but that seems hardly realistic as many libraries and applications are still lacking TLS 1.3 support.

That said, it is possible to have a fairly robust TLS setup using currently available versions, provided care is taken. In this article, we propose just that: how to create a modern and secure TLS setup, while maintaining an adequate level of compatibility. Additionally, we provide ready-to-use Terraform examples for a Docker-based nginx reverse proxy setup, with TLS 1.3 support, and Let’s Encrypt automatic certificate renewal, for both Amazon Web Services and Google Cloud Platform. This setup allows you to quickly enable TLS on any web application.

TL;DR;

If you are impatient, you can jump directly to the recommendations and examples.

An impatient Storm Trooper. Photo by John Moeses Bauan.
  • In the beginning, there was SSL 1.0. Not much public information is available. According to one source, several design flaws, such as missing data integrity and no replay protection, prevented SSL 1.0 from seeing the light of day;
  • SSL 2.0, the first public version, is eventually released in March 1995, as part of Netscape Navigator 1.1 browser. Several security issues are found, such as cipher downgrade and length-extension attacks;
  • SSL 3.0 is released in March 1996, with Netscape Navigator 2, fixing several vulnerabilities found in SSL 2.0;
  • TLS 1.0, a.k.a. SSL 3.1, is released in January 1999, after a standardization effort by the IETF. It is an incremental evolution over SSL 3.0, bringing no dramatic changes;
  • TLS 1.1 is released in April 2006. It includes mitigations to attacks on CBC ciphers. One particular change, explicit initialization vectors, will eventually prevent BEAST, five years into the future;
  • TLS 1.2 is released in August 2008. Changes include support for authenticated encryption with associated data (AEAD) ciphers, like AES-GCM, and stricter protocol validations;
  • TLS 1.3 is released in August 2018. There are many major differences from TLS 1.2, to the point that some believe it should be called TLS 2.0. We will briefly cover them below.

TLS is a protocol that provides a way for two parties to establish a secure communication channel between them.

That’s it.

But keep in mind that achieving this is no small feat. Look at TLS’ vulnerability history to see how hard it is.

TLS makes establishing a secure communication channel possible by providing three key services:

  • Confidentiality: ensures that data exchanged between peers is kept secret from third-parties. This is especially important for sensitive data, like passwords, credit cards, and the embarrassing contents of our shopping carts. Confidentiality is the characteristic that is most commonly associated with TLS, and its purpose is usually well understood;
  • Integrity: makes sure that data transmitted between peers is reliable and not tampered with during transit. Note that in the context of TLS, “integrity” refers to message authentication;
  • Authentication: ensures that clients communicate with legitimate servers. This is fundamental for assuring both confidentiality, and integrity, by providing trustworthy keying material for encryption and message authentication. Note that authentication and integrity are always important, whether the transmitted data is confidential or not. Optionally, TLS can also be used to authenticate clients, e.g. through client certificates, but this is less common;

One common criticism against TLS is that “TLS is slow”; for the vast majority of use cases, it is a misconception, even more so in TLS 1.3.

Another argument is that “All information on my website is public, so I do not need TLS”. A service providing public information may not require confidentiality, but authentication and integrity should never be optional. Otherwise, a user visiting an unprotected website has no guarantees about the authenticity of the information contained on it. The user may be attacked or, at best, annoyed by a potential man-in-the-middle — think Wi-Fi networks, or less ethical ISPs.

Some possible attack scenarios follow.

  • Subtly change a small, but critical, piece of information on a website. Such as a bank account number, cryptocurrency wallet address, phone number, email, and others;
  • Launch an “opportunistic cryptojacking” attack by injecting cryptocurrency mining code on the original webpage;
  • Redirect the victim to a phishing page to steal their credentials;
  • Inject ads and analytics/tracking javascript.

The list goes on.

We believe that it is a matter of responsibility towards users to ensure that communications are properly secured. Regardless of the information being confidential or not. We should be certain that our users are talking with us, and that the message we want to send them is not tampered with by a third party.

Photo by Terry Robinson.

TLS 1.3 is the new hotness and we are thrilled about it. Do not let the minor-only version increase fool you: this version is a significant departure from earlier TLS versions. It incorporates many lessons learned since the release of TLS 1.2, ten years ago. It aims to be both faster and safer.

Some highlights are:

  • Perfect-forward secrecy is now mandatory. From a security and privacy perspective, it is a very good thing. However, this raised some concerns from organizations who rely on passive TLS decryption for security, which TLS 1.3 breaks. This made some people very angry who regarded it as a bad move;
  • From TLS 1.0 to TLS 1.2, the handshake stayed mostly the same. Not anymore: TLS 1.3 comes with a redesigned, safer and faster 1-RTT handshake. In TLS 1.3, after the initial handshake messages, everything is encrypted. This means that even server certificates are encrypted. Eventually, combined with Encrypted SNI and DNS over HTTPS, an outside observer will not be able to determine which site a user visits (aside from the IP address, of course). Paired with a CDN, this feature may prove crucial for people who need to circumvent Internet censorship;
  • A ton of stuff was removed: renegotiation, compression, and many legacy algorithms: DSA, RC4, SHA1, MD5, CBC MAC-then-Encrypt ciphers — hopefully fixing CBC padding-oracles once and for all, and more;
  • Much improved resiliency against downgrade attacks;
  • Modern Daniel J. Bernstein algorithms are supported by default: x25519, Ed25519, and ChaCha20-Poly1305, which is great;
  • Session resumption and protocol extensions are significantly different. Session resumption using old-style “session IDs” and “session tickets” are gone. They have been replaced with a unified mechanism based on pre-shared keys (PSK).

If you wish to dig deeper, Cloudflare has done a great job clarifying the new protocol features on their blog here and here.
If you really want to go down the rabbit hole, take a look at the RFC, and this amazing TLS 1.3 byte-by-byte guide, created by Michael Driscoll.

In order to decide what configuration we should recommend, we take into account four different aspects:

  • Security — the recommendations should only include settings that are safe against known attacks;
  • Performance — without sacrificing security, higher-performance options should be preferred over lower-performance alternatives;
  • Compliance — if a major standard recommends against specific versions or setup, we will comply with it so boxes can be checked and peace of mind achieved;
  • Adoption — it would be great to recommend TLS 1.3 only, but we would be severely limiting the number of people who can talk with us. So a reasonable balance must be reached between security and adoption.

Implementation issues aside, the SSL and TLS protocols have had a rough history. This is not the fault of the protocol creators though. Several early design decisions were made when researchers had not yet figured out key aspects of secure protocol construction. One such example is the, at the time thought to be safe, MAC-then-Encrypt cryptographic composition. This design decision brought immense pain over the years in the form of padding-oracle attacks, and many creative TLS vulnerability names. Other examples exist, such as the need to accommodate export ciphers: there was a time where encryption was classified as a munition, and thus export restrictions applied. This requirement eventually led to vulnerabilities such as Logjam and FREAK. TLS history is very rich, and quite a few more examples exist.

The recommendations should take into account known attacks and, if possible, mitigate potential future attacks. Please note that the following list is not complete.

Renegotiation attacks — CVE-2009–3555 and CVE-2011–1473

These vulnerabilities are fixed in every non-ancient TLS library and server application. To our surprise, Probe.ly found new instances of CVE-2011–1473 affecting nginx and OpenSSL in the wild. After some investigation, we discovered that it was caused by a change in the OpenSSL 1.1 API. The issue was fixed in OpenSSL and nginx a few months ago. Be aware that, if you are using OpenSSL ≥ 1.1 with nginx stable (1.14 at this time), you are still vulnerable. So, considering the above, our recommendations are:

  • Ensure that your TLS libraries and servers are up-to-date;
  • Renegotiation is typically used for rekeying and requesting client certificates. Many major websites disable renegotiation altogether and, unless you have a specific need for it, we recommend you do too;
  • Regularly test your deployments to make sure that a regression is not introduced.

Padding-oracle attacks — POODLE, LUCKY13, ROBOT, and others

  • Ensure that your TLS libraries and applications are up-to-date;
  • Hardcore option: disable CBC ciphers completely. This requires TLS 1.2 with AEAD ciphers, or TLS 1.3. We understand that this may be a controversial recommendation because, even if we use TLS 1.2, enabling AEAD ciphers only may turn into a compatibility minefield. The motivation is that, over the years, many attacks have exploited CBC padding-oracles due to the problematic MAC-then-Encrypt composition. It has been a game of cat and mouse between security engineers and security researchers: researchers find new padding-oracles; engineers fix them, hoping that it’s the last time; repeat;
  • Use TLS 1.3. A source of padding-oracles has been the PKCS#1 v1.5 padding scheme used with RSA. TLS 1.3 uses the improved RSASSA-PSS scheme, which is much more robust.

Downgrade attacks — FREAK, Logjam, POODLE

  • Ensure that your TLS libraries and applications are up-to-date;
  • Ensure that applications enable TLS_FALLBACK_SCSV;
  • TLS 1.3 has the most reliable protection mechanism against these types of attacks, using, among other things, the ServerRandom value in a clever way.

Compression attacks — CRIME and BREACH

CRIME and BREACH are chosen-plaintext attacks, that leverage compression to create side-channels and recover the plaintext. Both attacks rely on code running on the victim’s computer to work, e.g. malicious JavaScript, and an eavesdropper that can observe encrypted traffic.

  • Ensure that your TLS libraries and applications are up-to-date;
  • For CRIME, disable TLS compression;
  • BREACH works at the application level and requires that secret data is echoed back in the page body, like CSRF tokens. The tradeoff between better performance provided by compression, and potential exposure to this attack should be weighted.

Insecure cipher attacks — 64-bit ciphers (Sweet32) and RC4

  • Disable all 64-bit block ciphers (DES, 3DES, others);
  • Disable RC4.

DROWN

  • Ensure that all your TLS libraries and servers are up-to-date. In some scenarios, DROWN relies on attacking old and unmaintained SSL 2.0 servers to compromise keys shared between both old and new TLS servers.

BEAST

  • Ensure that your clients (browsers) are updated. BEAST is a client-side attack;
  • Protocol-level solution: enable TLS ≥ 1.1 only.

Insecure protocol versions

  • All SSL versions have known vulnerabilities and should be disabled;
  • TLS 1.0 is vulnerable to BEAST, a client-side vulnerability and clients should be updated accordingly. However, there are no guarantees that all TLS 1.0 clients are patched. The preferable solution is to disable TLS 1.0;
  • If you can, we strongly recommend using AEAD ciphers only. This implies using TLS 1.2 and above.

Adequate key sizes

  • X.509 certificates: use RSA with at least 2048-bit keys; ECDSA with at least 256-bit keys; use a strong hash function for the signature algorithm, e.g. SHA256.
  • Key exchange: use ECDHE with least 256-bit keys;
  • Encryption: use AES with least 128-bit keys or ChaCha20, which uses 256-bit keys;
  • Integrity: Use HMAC with at least 256-bit keys, e.g. HMAC-SHA256 or Poly1305, which uses 256 bit keys.

Implementation errors — goto fail; Heartbleed, and many others

  • Ensure that your TLS libraries and applications are up-to-date :).

SSLstrip-like attacks

While not an issue with the TLS protocol itself, some attacks attempt to redirect the victim to a version of a website without HTTPS, thus without any security guarantee that TLS provides.

  • Enable HTTP Strict Transport Security (HSTS). This is an application configuration option, not a TLS setting.

AEAD ciphers — One great thing about TLS 1.2 is that it supports AEAD ciphers. This means that encryption and authentication are now tied into one cryptographic primitive. The best examples we have right now are AES-GCM, which can go really fast on CPUs supporting AES-NI; and ChaCha20-Poly1350, which is very fast, even in software, and is recommended for mobile devices. So, in addition to being more secure, TLS 1.2 is actually faster than earlier versions.

OCSP Stapling — Enables faster TLS handshakes by appending certificate revocation data during the initial handshake. Stapling allows clients to avoid making additional connections to verify certificate validity. This is not always possible to enable, as servers will require outbound connectivity.

TLS 1.3 1-RTT and 0-RTT handshakes — as discussed earlier, TLS 1.3 has a faster handshake that completes in 1-RTT. Additionally, it has a particular session resumption mode where, under certain conditions, it is possible to send data to the server on the first flight (0-RTT).

Starting June 30, 2018, PCI 3.1 forbids all SSL versions, and TLS versions before 1.1:

30 June 2018 is the deadline for disabling SSL/early TLS and implementing a more secure encryption protocol — TLS 1.1 or higher (TLS v1.2 is strongly encouraged) in order to meet the PCI Data Security Standard (PCI DSS) for safeguarding payment data.

NIST requirements are even stricter for government-only applications, requiring TLS 1.2 and above:

Servers that support government-only applications shall be configured to use TLS 1.2, and should be configured to use TLS 1.3. These servers should not be configured to use TLS 1.1, and shall not use TLS 1.0, SSL 3.0, or SSL 2.0.

According to recent sources, TLS 1.2 adoption is at around 94%. This gives us some confidence that disabling previous TLS versions is a viable option. In December 2017, Cloudflare reported that TLS 1.1 represented only 0.38% of total connections. The recent PCI Council requirement of having TLS 1.0 disabled to achieve compliance also helped to push the adoption level. Taking all this information into account, it seems reasonable to support only TLS 1.2 and above.

We suggest two modern configurations, both supporting TLS 1.2 and above. As discussed earlier, this seems like a reasonable decision in 2019. The first configuration is more lenient and provides better compatibility, while still using modern cryptography. The second configuration is very strict and disables CBC ciphers altogether. Note that the second option should work with modern browsers, but will fail, for example, with Java 7. If you know for sure that your traffic is predominantly coming from modern browsers and are willing to sacrifice the long-tail, or have control over both clients and servers, go a(h)ead.

Common recommendations

  • Enable TLS 1.2 and TLS 1.3;
  • Enable HTTP Strict Transport Security (HSTS);
  • Prefer server cipher order;
  • Enable OCSP stapling, if possible;
  • Disable TLS compression;
  • Disable TLS renegotiation;
  • As we found out with CVE-2011–1473, regularly test your deployments to make sure that a regression is not introduced.

In addition to the cipher suite names as defined by OpenSSL, we provide the standard IANA cipher suite names. This should make it easier to enable the configurations on other TLS libraries, such as NSS, GnuTLS, SChannel, and others.

Option A— Modern compatible TLS cipher suites

OpenSSL

TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256

IANA

TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

Option B — Modern strict TLS cipher suites

OpenSSL

TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

IANA

TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

We created an Nginx-based TLS reverse proxy container with modern TLS settings and automatic certificate renewals. You can find it here.

It might be one of these 🙄Photo by frank mckenna.

The reverse proxy can be placed in front of an existing HTTP website to enable HTTPS functionality with, hopefully, minimal hassle. You can think of it as a (limited) TLS terminator. You are free to change the configuration to better suit your needs, of course.

We provide two Terraform configurations that will automatically create the required cloud infrastructure and deploy the TLS reverse proxy. This way, you can easily try out the TLS configurations on AWS or GCP.

We also detail the manual steps required to run the examples on a stand-alone virtual machine. This assumes that the VM has an assigned public IP, and is reachable on ports 80 and 443.

Please note that these examples are meant to be used as proof-of-concept only. We are using “bleeding edge” package versions, since there is no official support for TLS 1.3 in the Nginx Docker images yet. The deployments run on a single-instance, and do not scale automatically. We advise you to not use this setup in production. In a next article, we plan to provide scalable examples for this setup, Kubernetes, and Amazon ECS.

With the exception of the stand-alone example, familiarity with AWS or GCP is required. If you have any issue running any of our example deployments, please let us know in the comments section. We will do our best to help you out.

Requirements

This setup needs a dedicated virtual machine with the following requirements:

  • Docker;
  • Docker Compose;
  • Systemd (available in most Linux distributions);
  • Public IP;
  • Public DNS hostname, pointing to the IP above;
  • Ports 80 and 443 available;
  • Ports 80 and 443 reachable from the Internet.

Procedure

Log in to the virtual machine and run the following commands:

git clone https://github.com/Probely/simple-tls-proxy
cd simple-tls-proxy
pushd deployments/stand-alone
cp sample/sample-deployment.env deployment.env

Next, edit the deployment.env file, and set the values according to your setup. Make sure that the LETSENCRYPT_EMAIL and PROXY_BACKEND_HOSTS variables are set correctly.

Finally, run these commands:

popd
sudo make install

You can check the logs with these commands:

# Let's Encrypt status messages
sudo journalctl -fu tls-proxy-controller
# Nginx logs
sudo journalctl -fu tls-proxy

If everything is configured correctly, you should see Let’s Encrypt log entries confirming that the certificate was fetched correctly.

Requirements

Procedure

Run these commands on the machine you have the AWS CLI and Terraform installed:

git clone https://github.com/Probely/simple-tls-proxy
cd deployments/aws

Edit the default.auto.tfvars file and set the following variables:

  • letsencrypt_email
  • backend_hosts
  • public_key

Specific guidance on how to choose proper values is provided inside the file. When all required variables are set, run the following:

terraform apply

Wait for Terraform to finish. It can take a few minutes.

Next, you need to determine your instance’s IP address.

# The region argument may not be needed, depending on your AWS CLI configuration.
PROXY_INSTANCE_IP=$(aws ec2 describe-instances --filters 'Name=tag:Name,Values=tls-proxy-playground' --query 'Reservations[].Instances[].PublicIpAddress' --output text --region eu-west-3)
echo $PROXY_INSTANCE_IP

Create a DNS entry with the hostname used in proxy_backend_hosts and point it to the IP address obtained above.

Wait for 5–10 minutes. The virtual machine requires some time to initialize and install all required packages.

If you want to check the progress or just look around, you can log in to the virtual machine using the SSH key defined inpublic_key.

ssh [email protected]$PROXY_INSTANCE_IP -i /path/to/key

If the yum process is still running in the VM, wait a bit more for the installation to finish.

After the installation ends, you can check the logs with these commands:

# Let's Encrypt status messages
sudo journalctl -fu tls-proxy-controller
# Nginx logs
sudo journalctl -fu tls-proxy

Requirements

Procedure

Run these commands on the machine you have the Google Cloud SDK and Terraform installed:

git clone https://github.com/Probely/simple-tls-proxy
cd deployments/gcp

Edit the default.auto.tfvars file and set the following variables.

  • project_id
  • letsencrypt_email
  • backend_hosts

Specific guidance on how to choose proper values is provided inside the file. When all required variables are set, run the following:

terraform apply

Wait for Terraform to finish. It can take a few minutes.

Next, you need to determine your instance IP address.

gcloud --format="value(networkInterfaces[0].accessConfigs[0].natIP)" compute instances list --filter tls-proxy-playground

Create a DNS entry with the hostname used in proxy_backend_hosts and point it to the IP address obtained above.

Wait 5–10 minutes. The virtual machine requires some time to initialize and install all required packages.

If you want to check the progress or just look around, you can log in to the machine using gcloud.

# The zone argument may not be needed, depending on your gcloud configuration.
gcloud compute ssh tls-proxy-playground --zone=europe-west4-a

If the yum process is still running in the VM, wait a bit more for the installation to finish.

After the installation ends, you can check the logs with these commands:

# Let's Encrypt status messages
sudo journalctl -fu tls-proxy-controller
# Nginx logs
sudo journalctl -fu tls-proxy

If you still feel all this is too much of a hassle to configure and maintain, Troy Hunt has created an easy guide on how to enable TLS on any website.

It leverages Cloudflare’s free “Universal SSL” service. It is almost configuration-free, and comes with automatic certificate renewals. We understand that this may not be the right solution for everyone, as it does not guarantee end-to-end security. Since traffic is decrypted at Cloudflare, you must decide if this is an acceptable trade-off for you. If your site only has public information, it might be an easier decision to make. It is however arguably better than having no TLS at all, especially because this solution mitigates more likely attack scenarios such as MiTM on open WiFi networks.

You reached the finish line!

That’s a wrap. We hope you enjoyed the article and found some of the examples useful. This is our humble attempt to try to make your life easier :)

If you any trouble setting up our examples or any other remarks please leave a comment and we will try to help you.