Update: following popular demand, the article now includes nginx commands 🙂
Update 2: thanks to jackalope from Hacker News, I added a missing Apache directive for the cipher suites.
Update 3: recent attacks on RC4 have definitely made it a bad choice, and ECDHE cipher suites got improvements.
SSL is slow. These cryptographic algorithms eat the CPU, there is too much traffic, it is too hard to deploy correctly. SSL is slow. Isn’t it?
HELL NO!
SSL looks slow, because you did not even try to optimize it! For that matter, I could say that HTTP is too verbose, XML web services are verbose too, and all this traffic makes the website slow. But, SSL can be optimized, as well as everything!
Slow cryptographic algorithms
The cryptographic algorithms used in SSL are not all created equal: some provide better security, some are faster. So, you should choose carefully which algorithm suite you will use.
The default one for Apache 2’s SSLCipherSuite directive is: ALL: !ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP
You can translate that to a readable list of algorithms with this command: openssl ciphers -v ‘ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP’
Here is the result:
DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1 DHE-DSS-AES128-SHA SSLv3 Kx=DH Au=DSS Enc=AES(128) Mac=SHA1 AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1 EDH-RSA-DES-CBC3-SHA SSLv3 Kx=DH Au=RSA Enc=3DES(168) Mac=SHA1 EDH-DSS-DES-CBC3-SHA SSLv3 Kx=DH Au=DSS Enc=3DES(168) Mac=SHA1 DES-CBC3-SHA SSLv3 Kx=RSA Au=RSA Enc=3DES(168) Mac=SHA1 DHE-RSA-SEED-SHA SSLv3 Kx=DH Au=RSA Enc=SEED(128) Mac=SHA1 DHE-DSS-SEED-SHA SSLv3 Kx=DH Au=DSS Enc=SEED(128) Mac=SHA1 SEED-SHA SSLv3 Kx=RSA Au=RSA Enc=SEED(128) Mac=SHA1 RC4-SHA SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=SHA1 RC4-MD5 SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=MD5 EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH Au=RSA Enc=DES(56) Mac=SHA1 EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH Au=DSS Enc=DES(56) Mac=SHA1 DES-CBC-SHA SSLv3 Kx=RSA Au=RSA Enc=DES(56) Mac=SHA1 DES-CBC3-MD5 SSLv2 Kx=RSA Au=RSA Enc=3DES(168) Mac=MD5 RC2-CBC-MD5 SSLv2 Kx=RSA Au=RSA Enc=RC2(128) Mac=MD5 RC4-MD5 SSLv2 Kx=RSA Au=RSA Enc=RC4(128) Mac=MD5 DES-CBC-MD5 SSLv2 Kx=RSA Au=RSA Enc=DES(56) Mac=MD5 EXP-EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH(512) Au=RSA Enc=DES(40) Mac=SHA1 export EXP-EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH(512) Au=DSS Enc=DES(40) Mac=SHA1 export EXP-DES-CBC-SHA SSLv3 Kx=RSA(512) Au=RSA Enc=DES(40) Mac=SHA1 export EXP-RC2-CBC-MD5 SSLv3 Kx=RSA(512) Au=RSA Enc=RC2(40) Mac=MD5 export EXP-RC4-MD5 SSLv3 Kx=RSA(512) Au=RSA Enc=RC4(40) Mac=MD5 export EXP-RC2-CBC-MD5 SSLv2 Kx=RSA(512) Au=RSA Enc=RC2(40) Mac=MD5 export EXP-RC4-MD5 SSLv2 Kx=RSA(512) Au=RSA Enc=RC4(40) Mac=MD5 export
28 cipher suites, that’s a lot! Let’s see if we can remove the unsafe ones first! You can see at the end of the of the list 7 ones marked as “export”. That means that they comply with the US cryptographic algorithm exportation policy. Those algorithms are utterly unsafe, and the US abandoned this restriction years ago, so let’s remove them:
‘ALL:!ADH:!EXP:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2’.
Now, let’s remove the algorithms using plain DES (not 3DES) and RC2: ‘ALL:!ADH:!EXP:!LOW:!RC2:RC4+RSA:+HIGH:+MEDIUM’. That leaves us with 16 algorithms.
It is time to remove the slow algorithms! To decide, let’s use the openssl speed command. Use it on your server, ecause depending on your hardware, you might get different results. Here is the benchmark on my computer:
OpenSSL 0.9.8r 8 Feb 2011 built on: Jun 22 2012 options:bn(64,64) md2(int) rc4(ptr,char) des(idx,cisc,16,int) aes(partial) blowfish(ptr2) compiler: -arch x86_64 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -O3 -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DMD32_REG_T=int -DOPENSSL_NO_IDEA -DOPENSSL_PIC -DOPENSSL_THREADS -DZLIB -mmacosx-version-min=10.6 available timing options: TIMEB USE_TOD HZ=100 [sysconf value] timing function used: getrusage The 'numbers' are in 1000s of bytes per second processed. type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes md2 2385.73k 4960.60k 6784.54k 7479.39k 7709.04k mdc2 8978.56k 10020.07k 10327.11k 10363.30k 10382.92k md4 32786.07k 106466.60k 284815.49k 485957.41k 614100.76k md5 26936.00k 84091.54k 210543.56k 337615.92k 411102.49k hmac(md5) 30481.77k 90920.53k 220409.04k 343875.41k 412797.88k sha1 26321.00k 78241.24k 183521.48k 274885.43k 322359.86k rmd160 23556.35k 66067.36k 143513.89k 203517.79k 231921.09k rc4 253076.74k 278841.16k 286491.29k 287414.31k 288675.67k des cbc 48198.17k 49862.61k 50248.52k 50521.69k 50241.28k des ede3 18895.61k 19383.95k 19472.94k 19470.03k 19414.27k idea cbc 0.00 0.00 0.00 0.00 0.00 seed cbc 45698.00k 46178.57k 46041.10k 47332.45k 50548.99k rc2 cbc 22812.67k 24010.85k 24559.82k 21768.43k 23347.22k rc5-32/12 cbc 116089.40k 138989.89k 134793.49k 136996.33k 133077.51k blowfish cbc 65057.64k 68305.24k 72978.75k 70045.37k 71121.64k cast cbc 48152.49k 51153.19k 51271.61k 51292.70k 47460.88k aes-128 cbc 99379.58k 103025.53k 103889.18k 104316.39k 97687.94k aes-192 cbc 82578.60k 85445.04k 85346.23k 84017.31k 87399.06k aes-256 cbc 70284.17k 72738.06k 73792.20k 74727.31k 75279.22k camellia-128 cbc 0.00 0.00 0.00 0.00 0.00 camellia-192 cbc 0.00 0.00 0.00 0.00 0.00 camellia-256 cbc 0.00 0.00 0.00 0.00 0.00 sha256 17666.16k 42231.88k 76349.86k 96032.53k 103676.18k sha512 13047.28k 51985.74k 91311.50k 135024.42k 158613.53k aes-128 ige 93058.08k 98123.91k 96833.55k 99210.74k 100863.22k aes-192 ige 76895.61k 84041.67k 78274.36k 79460.06k 77789.76k aes-256 ige 68410.22k 71244.81k 69274.51k 67296.59k 68206.06k sign verify sign/s verify/s rsa 512 bits 0.000480s 0.000040s 2081.2 24877.7 rsa 1024 bits 0.002322s 0.000111s 430.6 9013.4 rsa 2048 bits 0.014092s 0.000372s 71.0 2686.6 rsa 4096 bits 0.089189s 0.001297s 11.2 771.2 sign verify sign/s verify/s dsa 512 bits 0.000432s 0.000458s 2314.5 2181.2 dsa 1024 bits 0.001153s 0.001390s 867.6 719.4 dsa 2048 bits 0.003700s 0.004568s 270.3 218.9
We can remove the SEED and 3DES suite because they are slower than the other. DES was meant to be fast in hardware implementations, but slow in software, so 3DES (which runs DES three times) is slower. On the contrary, AES can be very fast in software implementations, and even more if your CPU provides specific instructions for AES. You can see that with a bigger key (and so, better theoretical security), AES gets slower. Depending on the level of security, you may choose different key sizes. According to the key length comparison, 128 might be enough for now. RC4 is a lot faster than other algorithms. AES is considered safer, but the implementation in SSL takes into account the attacks on RC4. So, we will propose this one in priority. Following recent researches, it appears that RC4 is not safe enough anymore. And ECDHE got a performance boost with recent versions of OpenSSL. So, let’s forbid RC4 right now!
So, here is the new cipher suite: ‘ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM’
And the list of ciphers we will use:
DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1 DHE-DSS-AES128-SHA SSLv3 Kx=DH Au=DSS Enc=AES(128) Mac=SHA1 AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1 RC4-SHA SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=SHA1 RC4-MD5 SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=MD5 RC4-MD5 SSLv2 Kx=RSA Au=RSA Enc=RC4(128) Mac=MD5
9 ciphers, that’s much more manageable. We could reduce the list further, but it is already in a good shape for security and speed. Configure it in Apache with this directive:
SSLHonorCipherOrder On
SSLCipherSuite ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM
Configure it in Nginx with this directive:
ssl_ciphers ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM
You can also see that the performance of RSA gets worse with key size. With the current security requirements (as of now, January 2013, if you are reading this from the future). You should choose a RSA key of 2048 bits for your certificate, because 1024 is not enough anymore, but 4096 is a bit overkill.
Remember, the benchmark depends on the version of OpenSSL, the compilation options and your CPU, so don’t forget to test on your server before implementing my recommandations.
Take care of the handshake
The SSL protocol is in fact two protocols (well, three, but the first is not interesting for us): the handshake protocol, where the client and the server will verify each other’s identity, and the record protocol where data is exchanged.
Here is a representation of the handshake protocol, taken from the TLS 1.0 RFC:
Client Server ClientHello --------> ServerHello Certificate* ServerKeyExchange* CertificateRequest* <-------- ServerHelloDone Certificate* ClientKeyExchange CertificateVerify* [ChangeCipherSpec] Finished --------> [ChangeCipherSpec] <-------- Finished Application Data <-------> Application Data
You can see that there are 4 messages exchanged before any real data is sent. If a TCP packet takes 100ms to travel between the browser and your server, the handshake is eating 400ms before the server has sent any data!
And what happens if you make multiple connections to the same server? You do the handshake every time. So, you should activate Keep-Alive. The benefits are even bigger than for plain unencrypted HTTP.
Use this Apache directive to activate Keep-Alive:
KeepAlive On
Use this nginx directive to activate keep-alive:
keepalive_timeout 100
Present all the intermediate certification authorities in the handshake
During the handshake, the client will verify that the web server’s certificate is signed by a trusted certification authority. Most of the time, there is one or more intermediate certification authority between the web server and the trusted CA. If the browser doesn’t know the intermediate CA, it must look for it and download it. The download URL for the intermediate CA is usually stored in the “Authority information” extension of the certificate, so the browser will find it even if the web server doesn’t present the intermediate CA.
This means that if the server doesn’t present the intermediate CA certificates, the browser will block the handshake until it has downloaded them and verified that they are valid.
So, if you have intermediate CAs for your server’s certificate, configure your webserver to present the full certification chain. With Apache, you just need to concatenate the CA certificates, and indicate them in the configuration with this directive:
SSLCertificateChainFile /path/to/certification/chain.pem
For nginx, concatenate the CA certificate to the web server certificate and use this directive:
ssl_certificate /path/to/certification/chain.pem
Activate caching for static assets
By default, the browsers will not cache content served over SSL, for security. That means that your static assets (Javascript, CSS, pictures) will be reloaded on every call. Here is a big performance failure!
The fix for that: set the HTTP header “Cache-Control: public” for the static assets. That way, the browser will cache them. But don’t activate it for the sensitive content, beacuase it should not be cached on the disk by your browser.
You can use this directive to enable Cache-Control:
<filesMatch ".(js|css|png|jpeg|jpg|gif|ico|swf|flv|pdf|zip)$"> Header set Cache-Control "max-age=31536000, public" </filesMatch>
The files will be cached for a year with the max-age option.
For nginx, use this:
location ~ \.(js|css|png|jpeg|jpg|gif|ico|swf|flv|pdf|zip)$ { expires 24h; add_header Cache-Control public; }
Update: it looks like Firefox ignores the Cache-Control and caches everything from SSL connections, unless you use the “no-store” option.
Beware of CDN with multiple domains
If you followed a bit the usual performance tips, you already offloaded your static assets (Javascript, CSS, pictures) to a content delivery network. That is a good idea for a SSL deployment too, BUT, there are caveats:
- your CDN must have servers accessible over SSL, otherwise you will see the “mixed content” warning
- it must have “Keep-Alive” and “Cache-control: public” activated
- it should serve all your assets from only one domain!
Why the last one? Well, even if multiple domains point to the same IP, the browser will do a new handshake for every domain. So, here, we must go against the common wisdom of separating your assets on multiple domains to profit from the parallelized request in the browser. If all the assets are served from the same domain, there will only be one handshake. It could be fixed to allow multiple domains, but this is beyond the scope of this article.
More?
I could talk for hours about how you could tweak your web server performance with SSL. There is alot more to it than these easy tips, but I hope those will be of useful for you!
If you want to know more, I am currently writing an ebook about SSL tuning, and I would love to hear your comments about it!
If you need help with your SSL configuration, I am available for consulting, and always happy to work on interesting architectures.
By the way, if you want to have a good laugh with SSL, read “How to get a certificate signed by multiple certification authorities” 🙂
It would be amazing if you knew how to do this for IIS7 as well. Just sayin’. Amazing insight on the subject, either way!
Nope, sorry, I don’t know IIS, but if you can find the right configuration options, I will add them to the post!
Great article.
Presenting the intermediate certificates is something that you should be doing anyway (I believe the TLS RFC requires it), especially since Chrome for Android (and probably others too) seem to have retrieving intermediates turned off.
Consider SPDY, too. Nginx has a module for SPDY/2 and Apache has one (I think) – it can help speed up connections from compatible browsers, and paves the way for a bunch of other useful speedups too.
Presenting the intermediate certificates can solve a lot of weird bugs, too. I had one with a client changing a certificate signed by Verisign, where the CA root had changed in the meantime. Fun…
SPDY is a good idea, and I’ll write about it in the future.
I found an article for IIS which allows me to disable weak encryption and force SSL 3.0. Thought I’d share for those of us who are .net developers.
http://blogs.iis.net/sakyad/archive/2008/12/11/enforcing-ssl-3-0-and-removing-weak-encryption-vulnerability-over-ssl-iis-6-0-and-isa.aspx
Thank you, that’s really useful!
You probably want to include the security side of SSL/TLS (as well as speed). The below will take care of the BEAST and CRIME attacks. In Apache, you probably want something like this:
SSLHonorCipherOrder On
SSLCipherSuite ‘RC4-SHA:AES+DH:!ADH’
SSLProtocol All -SSLv2
SSLCompression off
SSLInsecureRenegotiation off
Acelerando transações SSL | Blog de Programação Literati
Bookmarks for January 25th | Chris’s Digital Detritus
Thanks for writing this!
For others using HAProxy with SSL you can specify the ciphers as part of the bind option.
e.g.
bind :443 ssl crt meep.io.pem ciphers AES:ALL:!EXP:!LOW:!RC2:!3DES:!aNULL:!eNULL:+RC4:@STRENGTH
And you can set the keep-alive timeout for http with:
timeout http-keep-alive 100s
http://haproxy.1wt.eu/download/1.5/doc/configuration.txt
Any tips for folks using Amazing ELBs to handle SSL… they aren’t as fast as some pure Apache implementations.
I am not familiar with ELB, but it looks like this article would be of use to you: http://harish11g.blogspot.fr/2012/03/ssl-offloading-elastic-load-balancing.html
What are your thoughts on ioerrors duraconf settings?
https://github.com/ioerror/duraconf/tree/master/configs/apache2
These settings are pretty good from a security point of view. He includes the HSTS header, and adds the HTTP -> HTTPS redirection code for websites where SSL was added recently.
But please be aware that the cipher suite selection depends on his particular OpenSSL version. I get the same cipher suites with OpenSSL 0.9.8r, but with OpenSSL 1.0.1, I get 64 different cipher suites (mostly because I have elliptic curves, SRP and Camellia algorithms).
That’s why I gave the commands, so that people can test and make their own mind.
Did you deploy the duraconf settings on your servers?
Server Admin | Annotary
Interesting that ECDHE was not mentioned. I take it that it’s for newer versions only?
I found that was too confusing for that first post. For more information on ECDHE, see this comment on HN: http://news.ycombinator.com/item?id=5116254
Web | Annotary
Thanks for the the awesome post.
One quick note: a lot of the OpenSSL commands use “smart quotes” which makes them no fun to copy and paste into a shell.
It looks like WordPress transformed my simple quotes 😦
Very informative and easy to follow guide. Will try these tips out. Cheers.
Run this same check with a newer versions of openssl. In .9.8 your cipher list has 9, as stated. In OpenSSL 1.0.1 14 your ciphers hit a total of 66.
That’s why I gave the commands to do the benchmark. It depends on the OpenSSL version, the compilation options, the presence of AES-NI…
加快 SSL 加解密速度… | Gea-Suan Lin's BLOG
SSL with Rails | Pearltrees
Hi,
the default ciphers for Nginx are HIGH:!aNULL:!MD5 (cf http://wiki.nginx.org/HttpSslModule), so isn’t your directive counter-productive as you’re adding more ciphers ?
Thanks you !
The goal was not to reduce the number of ciphers, but to select a few efficient ones. The Nginx default still uses 3DES, whch was deemed slow, does not provide RC4, which is fast and protects against the BEAST attack, and prevents the usage of MD5 for the MAC. MD5 should not be used to sign certificates, but it might (don’t take my word for it, I have not yet confirmed that) be safe, for now, as a MAC for SSL record messages, with the added benefit that MD5 is faster than SHA1.
That’s clearer, thanks.
Weekend Links : Alban Crommer
Looking for big architectures and adventurous sysadmins – Unhandled expression
Web Performance 101: An opinionated guide to the 22 links that every developer should read
Sysadmin Sunday 113 - Server Density Blog
openssl ciphers -v ‘ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:RC4+RSA:+HIGH:+MEDIUM’ gives me 66 (!) ciphers on Ubuntu but just a few on my Mac.
Not quite sure where to start to cut it down to a manageable number. Should I just go by their speed or are there some ciphers that I should get rid of first?
Yes, because Linux hosts have a more recent OpenSSL versions, and a lot of delicious ciphers to taste 🙂
If you remove AES 256 (which, from a theoretical standpoint, has more attacks than AES 128), and Diffie Hellman key exchange, which is slower than elliptic curve Diffie Hellman. Here is the command: openssl ciphers -v ‘ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!AES256:!DH:RC4+RSA:+HIGH:+MEDIUM’
With this, you’re left with 30 ciphers. Mostly, you have Camellia, AES 128 and RC4 for transport encryption, ECDH, SRP and RSA for the key exchange. The cipher chosen will depend on a lot of things, like the client support, which type of key you use in your certificate, etc.
I don’t have enough data on the client support of ciphers (yet), but there will be more in the ebook, once it is written 🙂
I’m on a stock centos 6 box.
When I add this code to my apache config
SSLHonorCipherOrder On
SSLCipherSuite ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:RC4+RSA:+HIGH:+MEDIUM
IE9 (and maybe other versions of IE) stop displaying the page (I get no discernable error messages or headers, just “IE cannot display the webpage”
Hi Geal!
Cool paper!
But I’m looking for similar info about Tomcat6.
I try to test allowed ciphers but it doesn’t seem to work.
For example, I try to allow only very strong ciphers like AES-256.
I added this line in my SSL connector section in server.xml of Tomcat6:
ciphers=”DHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,AES256-SHA”
I stopped/started the server.
With IE7+XP SP3, I manage to reach an HTTPS page only with RC4 128 cipher !?!
I would expect an error message like “your browser doesn’t support AES 256 required by server”.
What could be the problem ?
Regards,
Steve92
Web Performance Resources and Optimization Tools | TechSlides
I am having a problem on nginx with ssl renegotiation with safari. Is there a way to fix that. Can we disable ssl renegotiation on nginx just like we can do on apache?
Maybe this will help: stackoverflow.com/questions/19992279/how-to-disable-tls-renegotiation-in-nginx
Good article, but it is missing one of the biggest SSL acceleration options available – session renegotiation and session resumption. the best way to handle a slow handshake is to avoid it in the firstplace. this is possibly trivial or even on by default with one web server, but add more and suddenly they have to sync ssl session data between them, which requires special configuration.
I did not talk about renegotiation mainly because it is harder to deploy. The goal was to present easy tips. Also, using keep-alive will help a lot already in reducing the number of handshakes.
Here is my Waterfall http://www.webpagetest.org/result/140417_42_H0D/1/details/
It is taking too much time in SSL negotiations.
I tried your first command in SSH:
openssl ciphers -v ‘ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP’
But I’m receiving error:
-bash: !ADH: event not found
Replace the quotes in the command you used, they are not valid