Hardening Your Web Server’s SSL Ciphers

There are many wordy articles on configuring your web server’s TLS ciphers. This is not one of them. Instead I will share a configuration which is both compatible enough for today’s needs and scores a straight “A” on Qualys’s SSL Server Test.

Disclaimer: I’m updating this post continually in order to represent what I consider the best practice in the moment – there are way too many dangerously outdated articles about TLS-deployment out there already.

Therefore it may be a good idea to check back from time to time because the crypto landscape is changing pretty quickly at the moment. You can follow me on Twitter to get notified about noteworthy changes.

If you find any factual problems, please reach out to me immediately and I will fix it ASAP.

Rationale

If you configure a web server’s TLS configuration, you have primarily to take care of three things:

  1. disable SSL 2.0, and – if you can afford it – SSL 3.0 (Internet Explorer 6 is the last remaining reason to keep it around; you can’t have elliptic curve crypto with SSL 3.0 and downgrade attacks exist),
  2. disable TLS 1.0 compression (CRIME),
  3. disable weak ciphers (DES, RC4), prefer modern ciphers (AES), modes (GCM), and protocols (TLS 1.2).

You should also put effort into mitigating BREACH. That’s out of scope here though as it’s largely application-dependent.

Software and Versions

On the server side, you should update your OpenSSL to 1.0.1c+ so you can support TLS 1.2, GCM, and ECDHE as soon as possible. Fortunately that’s already the case in Ubuntu 12.04 and later.

On the client side, the browser vendors are starting to catch up. As of now, Chrome 30, Internet Explorer 11 on Windows 8, Safari 7 on OS X 10.9, and Firefox 26 all support TLS 1.2.

RC4

There used to be a bullet point suggesting to use RC4 to avoid BEAST and Lucky Thirteen. And ironically, that used to be the original reason for this article: when Lucky Thirteen came out, the word in the streets was: “use RC4 to mitigate” and everyone was like “how!?”.

Unfortunately shortly thereafter, RC4 was found broken in a way that makes deploying TLS with it nowadays a risk. While BEAST et al require an active attack on the browser of the victim, passive attacks on RC4 ciphertext are getting stronger every day. In other words: it’s possible that it will become feasible to decrypt intercepted RC4 traffic eventually and the NSA probably already is. Microsoft even issued a security advisory that recommends to disable RC4.

The String

Until recently, Qualys preferred RC4 over CBC-mode ciphers and I gave you two cipher strings to choose from: one that gave you an “A” but used RC4 and one that gave you a “B” but was actually secure. Since they finally changed their mind – and as of Safari 7 there’s no mainstream browser left that is susceptible to BEAST – I can jump directly to the secure one:

ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS

You can test it against your OpenSSL installation using

openssl ciphers -v 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS'

to see what’s supported.


Please note that the !DSS at the end is only for cosmetic reasons to keep the output of openssl ciphers shorter. It has no implication security-wise because the signature-algorithm is determined by the type of your server key. DSS is not commonly deployed in the wild and if you don’t want your server to use it, simply don’t use a DSS server key (which is very unlikely to happen anyway). The current widespread standard is RSA while ECDSA is trying to get traction.


You’ll get:


The string also prefers AES-256 over AES-128 (except for GCM which is preferred over everything else). It does so mostly for liability reasons because customers may insist on it for bogus reasons.

However quoth a cryptographer:

AES-128 isn't really worse than AES-anythingelse, at least not in ways you care about

The very simplified gist here is that the only reason for having 256 bit keys are quantum computers which are less likely to become a problem than the key scheduling issues in AES-256. But let me stress that both are fine. It’s just that adding AES-256 ciphers doesn’t improve your security in practice.

So if AES-128 is fine for you, feel free to add an ‘:!AES256’ to the end of the cipher string like I do.

Apache

1
2
3
SSLProtocol ALL -SSLv2
SSLHonorCipherOrder On
SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS

This works on both Apache 2.2 and 2.4. If your OpenSSL doesn’t support the preferred modern ciphers (like the still common 0.9.8), it will fall back gracefully but your configuration is ready for the future.

Please note: you need Apache 2.4 for ECDHE and ECDSA. You can circumvent that limitation by putting an SSL proxy like stud or even nginx in front of it and let Apache serve only plain HTTP.

TLS compression is a bit more complicated: as of Apache 2.2.23, it’s not possible to switch it off inside of Apache. For Apache 2.2.24+ and 2.4.3+, you can switch it off using:

1
SSLCompression Off

Currently the default is On, but that changed from 2.4.4 on.

The good news for Ubuntu admins is that Ubuntu has back ported that option into their 2.2 packages – and set it to off by default – so you should be fine. The solution on Red Hat based OS (RHEL, Fedora, CentOS, Scientific Linux…) is setting an environment variable inside of your Apache startup script:

1
export OPENSSL_NO_DEFAULT_ZLIB=1

nginx

1
2
3
ssl_prefer_server_ciphers On;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;

SSL 2.0 is off and the best protocols on by default. However it may be that you have some artifact from pre-TLS 1.2 times lurking somewhere in your config so it’s better to be explicit.

TLS compression depends on the version of nginx and the version of OpenSSL. If OpenSSL 1.0.0 or later is installed, anything after nginx 1.0.9 and 1.1.6 is fine. If an older OpenSSL is installed, you’ll need at least nginx 1.2.2 or 1.3.2.

For more details, have a look at this serverfault answer.

TL;DR on TLS compression & nginx: if you’re using Ubuntu 12.04 or later you’re fine (OpenSSL 1.0.1/nginx 1.1.19).

CentOS/Red Hat Enterprise Linux 6

Although CentOS 6.5 is shipping an OpenSSL that is capable of ECDHE key exchange, their nginx is compiled against an older one. Therefore openssl ciphers will confusingly show you all the shiny ciphers but nginx just won’t offer it to the clients.

That’s terrible because it costs you PFS for IE browsers.

You’ll have to compile nginx yourself or source it from an alternative package index.

Bonus Points

Qualys updated their requirements on 2014-01-21 and the cipher suites here are still “A”–material. If you want an “A+” though, you’ll need to add HSTS headers too, which is out of scope for this article but the linked Wikipedia article will get you started.

Finally

Make sure to test your server afterwards!

If you want to learn more about deploying SSL/TLS, Qualys’s SSL/TLS Deployment Best Practices are a decent primer.

For investigating the SSL/TLS behavior of your browser, How’s My SSL? will give you all the details you need.

The (Near) Future

2013 has galvanized the whole industry. This is a good thing. In 2012 barely anyone lost a thought about configuring their TLS ciphers, how many bits their certificates had, or even forward secrecy. That made it way too easy for the bad folks. Nowadays, people are questioning their own practices, open source projects work on enhancing their TLS support, and the public started to listen to cryptographers again instead of discounting them as crazy tinfoil crowd.

Good things are shaping on the horizon and Google’s Adam Langley given the power of having control over both servers and the most popular browser is pressing ahead. Their servers widely support TLS 1.2 with AES-GCM. Chrome has the best TLS support already. Additionally, its Canary releases now have grown support for ChaCha20 which is an extremely fast yet secure stream cipher by Dan Bernstein and Poly1305 a great MAC of the same pedigree.

Now if people just stopped using old browsers and we could roll out SNI and mandatory TLS 1.2.

History

Since I’m keeping this up to date, I’m going to document changes for returning visitors:


I spoke at PyCon 2014 in Montreal about the Sorry State of SSL; you may want to check out the talk page and video!


Credits: The initial version of this article has been kindly proof-read by Christian Heimes. My current cipher string is based on one by Zooko Wilcox-O'Hearn who contrary to me is a cryptographer and has just launched a great secure cloud storage you should check out. The errors are all still mine.

← See all posts