OpenSSL supports a number of different SSL ciphersuites, which can have a huge impact on the overhead that SSL imposes on HTTP traffic.
Below I approach SSL from the performance side of things. Sites deploy HTTPS for one of two reasons:
- Financial or highly sensitive data is involved, there are regulations mandating security policies, and it would be extremely advantageous to someone if they obtained the data you’re transfering (even if it takes a year to crack the encryption).
- To avoid Firesheep style attacks or for integration with SSL secured services (e.g. Twitter). Nefarious persons might like to acquire the data, but they are probably not going to spend millions of dollars to crack your encryption.
The second use case is far more common. For this type of deployment performance is key, and security is a bit of a secondary concern (which isn’t to say you should be insecure). If you fall into the first category, I presume you are already well aware of the procedures required of you.
The following ciphers are supported in TLSv1:
- 3DES has historically been the preferred cipher for high security HTTPS transactions. It has been slow since the dawn of time, although it has ubiquitous browser support.
- RC4 is considered less secure than 3DES, but is similarly very well supported.
- AES is a newer cipher, formerly Rijndael, which won a contest to become the standardized replacement for DES.
Other ciphers exist, but I don’t feel that they’re suitable for various reasons.
It hopefully goes without saying, but under no circumstances should you allow SSLv2 connections in 2011 — SSLv3 was standardized in 1996! SSLv3 and TLSv1 are both acceptable.
This test was run with a single 64-bit instance of openssl speed on an Intel Xeon E5620. Assembly instructions were disabled for AES and RC4 ciphers per OpenSSL: Outmoded Assembly.
100,000 Kbyte/s is my threshold for acceptable performance. This represents 1 CPU core (of 8 in my case) running at 100% utilization to transfer 780Mbit/s of data (which is a reasonable saturation point for a gigabit Ethernet link).
This eliminates Camellia-256, SEED, and 3DES.
RC4 is the fastest cipher, if you are using a processor which does not support AESNI.
AES-128 is the next fastest cipher, and much faster than RC4 if you have AESNI support. It’s about 54% slower if you don’t. AES-256 is slower still, and unless explicitly configured otherwise, any browser that supports AES-128 will also support AES-256.
Camellia-128 is a tad slower still. It’s quite unlikely that any browser would only support Camellia so it is currently not essential.
NOTE: openssl-0.9.x has different cipher performance: Camellia-128 is 4x slower, and RC4 is slower in very old 0.9.x releases. For this and other reasons, you should be using openssl-1.0.x.
By default, OpenSSL negotiates the cipher to use like this:
- The user’s browser sends its cipherlist.
- This is compared with the server’s cipherlist1.
- The first cipher in the user’s list that the server supports is utilized for the connection.
The downside here is that it places the end user in control. From a performance standpoint, we want to be calling the shots here.
This behavior can be changed with flag SSL_OP_CIPHER_SERVER_PREFERENCE to SSL_CTX_set_options in your application. Apache’s mod_ssl has support for it as option SSLHonorCipherOrder.
This changes the behavior to prefer the first shared cipher using the order specified in the server’s cipherlist. For example:
client: 3des, aes-256-cbc, aes-128-cbc, rc4 server: aes-128-cbc, aes-256-cbc, rc4, 3des
Without this flag, 3des is selected. With it set, aes-128-cbc is selected. That client connection might use 97.5% less CPU to encrypt the data stream as a result.
For servers with AESNI (Intel Westmere, AMD Bulldozer, and some others), I recommend:
AES-128, RC4, AES-256, Camellia-128
For servers without AESNI (or without an OpenSSL patched to support AESNI):
RC4, AES-128, AES-256, Camellia-128
In order to do this, you’ll need to set your cipher string in your application’s configuration. You can see what ciphers will be matched by a string with openssl ciphers -v ‘STRING’, e.g.:
openssl ciphers -v ‘AES128-SHA:RC4:AES:CAMELLIA128-SHA:!ADH:!aNULL:!DH:!EDH:!eNULL:!LOW:!SSLv2:!EXP:!NULL’
will select AES-128, RC4, AES-256, Camellia-128 in that order.
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 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 CAMELLIA128-SHA SSLv3 Kx=RSA Au=RSA Enc=Camellia(128) Mac=SHA1
This can be configured in Apache with SSLCipherSuite.
How widely is AES supported?
All modern browsers2 support AES, however there is one big caveat: any browser which uses the system SSL services on Windows XP or older does not support AES. This includes:
- Internet Explorer
but not Firefox or Opera. Windows Vista and newer do support AES at the OS level.
This is a big motivator to continue permitting RC4, because the only other cipher in common use that these browsers are likely to support is 3DES.
Why disable 3DES?
You may be wondering: why not just permit 3DES as a fallback cipher at the end of my cipher string?
Consider the following potential DOS Attack:
- A large botnet of (unknowingly) controlled PCs is configured to permit only 3DES
- These users continue using your service as they normally would, any nothing appears out of the ordinary to them (and there is no compromise in over-the-wire security)
- Your servers are now processing this traffic 95% slower than normal
How would you handle a sudden need for 2x the processing power to serve the same amount of traffic?
While this scenario is unlikely to occur, it does highlight the extreme potential for denial or degradation of service simply as a result of the processing overhead of 3DES vs. any other cipher.
With that said, if you know that you have clients that cannot support any cipher but 3DES or if RC4 is not secure enough for your needs, then you should add it to your cipher list.
SSL session resume
If you have a VIP with Nehalem and Westmere servers behind it where any of the hosts might answer an HTTPS request to the same public IP, you must have a consistent SSL cipher list across all of them. This is somewhat mitigated by the standard practice of assigning SSL connections in a sticky fashion on load balancers, but ideally you would have eliminated stickiness by deploying a shared SSL session cache between all of the hosts.
Clients may open a new HTTPS connection (SSL session resume) which re-uses the previously negotiated session options, including the cipher. If all of the servers can resume the session (because they hold the original session id and data in cache), then you run the risk of a client who negotiated on a Westmere host and thus agreed to AES-128 being sent to a Nehalem host for its next connection where it should have agreed to RC4.
Either prefer RC4 across all hosts behind this VIP, or divide your hosts between multiple public facing IPs.
- Use equipment that supports AESNI (or prefer RC4)
- Enforce server side cipher preference
- Pare down your cipher list, eliminate extremely slow ciphers like 3DES and insecure ciphers like export versions
- Use openssl-1.0.x, in 64-bit mode