Adam Innes · Blog

Forward Secrecy for Everyday HTTPS: Put ECDHE and DHE First

· 7 min · security, tls, nginx, apache

The surveillance disclosures that started in June have a lot of people who run ordinary websites asking a question that used to come up mostly among cryptographers: if someone is recording my HTTPS traffic today, what happens if my server’s private key leaks next year? With the most common setup, every recorded session opens up. There’s a fix for that called forward secrecy, it’s mostly a cipher configuration change, and it’s worth understanding before you flip it on.

What a stolen key unlocks

When Google wrote about this in 2011, it said most major HTTPS sites ran without forward secrecy, which in practice means plain RSA key exchange. In the handshake described by RFC 5246, the client makes up a 48-byte premaster secret, encrypts it with the public key from the server’s certificate, and sends it over. The server decrypts it with its private key, and both sides derive the session keys from that secret plus the random values from the hello messages, which travel in the clear.

That’s efficient, and it means the certificate key is the only thing standing between a recording of the handshake and the session keys. The security analysis appendix of RFC 5246 says it directly: compromise of the server’s static RSA key means a loss of confidentiality for all sessions protected under that key. It doesn’t matter whether the key leaks through a break-in or a careless backup, or whether it happens years after the traffic was captured. Anyone holding the recordings and the key can go back and read them.

Ephemeral keys change the math

Forward secrecy means a later compromise of the long-term key doesn’t expose past sessions. In TLS you get it from ephemeral Diffie-Hellman. With a DHE suite, the server generates a temporary Diffie-Hellman key pair, signs the public half with its certificate key, and the client answers with its own public value. Both sides compute the same secret, and nobody on the wire ever sees it. RFC 5246 recommends generating a fresh private value for every handshake and notes that this is exactly what provides forward secrecy.

ECDHE does the same thing on an elliptic curve. RFC 4492 defines the elliptic curve suites for TLS and says ECDHE_RSA and ECDHE_ECDSA provide forward secrecy, while the fixed ECDH variants don’t. The useful detail for most of us is that ECDHE_RSA lets a server keep its existing RSA certificate. You don’t need a new cert. The certificate key’s job shrinks to signing the handshake, and a signature key stolen later can’t decrypt anything, because the secrets it signed for were thrown away when each session ended.

This isn’t new or exotic. In November 2011 Google announced on its security blog that it was enabling forward secrecy by default for Gmail and other HTTPS services like SSL Search, Docs and Google+, using ECDHE_RSA key exchange. The post pointed out the long game: traffic recorded today could be decrypted a decade later if the server key is ever broken, and with forward secrecy not even the server operator can decrypt old sessions. It also noted that Chrome, Firefox and Internet Explorer on Vista or later support elliptic curve Diffie-Hellman.

What it costs

Forward secrecy isn’t free, and the specs say so. RFC 4492 notes that a server’s computational cost is higher for ECDHE_RSA than for plain RSA key exchange, and the reason is easy to see: the server still does an RSA private key operation, now a signature, and adds elliptic curve Diffie-Hellman work on top. Classic DHE costs more again at comparable strength. The key size table at the top of RFC 4492 puts 1024-bit Diffie-Hellman level with 163-bit elliptic curves at about 80 bits of symmetric security, and 2048-bit level with 233-bit curves at 112 bits, and the RFC says the smaller key sizes save computational cost.

Google’s choice reflects that. In the comments under the 2011 announcement, a reply posted as agl said they picked ECDHE for its speed, that ephemeral Diffie-Hellman in a 2048-bit group is plenty secure but much slower, and that they didn’t support DHE at all for speed reasons. OpenSSL 1.0.1 also added optional 64-bit optimized implementations of the NIST P-224, P-256 and P-521 curves, credited in the CHANGES file to Google engineers, which need gcc 4.4 or later on a 64-bit build and the enable-ec_nistp_64_gcc_128 Configure option.

The cost is per full handshake, so session resumption softens it, which is why the nginx docs already recommend a shared session cache to reduce processor load. If you want numbers for your own hardware, openssl speed rsa2048 ecdhp256 compares the two operations. For most sites I’d expect the answer to be “turn it on,” with ECDHE preferred and DHE as a fallback.

What your stack needs

Start with OpenSSL. Version 0.9.8 carried elliptic curve suites from an early draft and kept them out of ALL. The CHANGES file shows that 1.0.0, released in March 2010, stopped excluding them from ALL and DEFAULT and added the EECDH alias for ephemeral ECDH suites, next to the existing EDH for ephemeral DH. So you want 1.0.0 or later, and 1.0.1e from February is the current release.

On nginx, the CHANGES for 1.0.x list ECDHE key exchange support in 1.0.6, and it arrived in 1.1.0 on the development branch, so the current 1.4.2 stable has it. There’s an ssl_ecdh_curve directive that the module page doesn’t document yet; in the 1.4.2 source it defaults to prime256v1, so you don’t need to touch it. For DHE, ssl_dhparam loads your own parameters. Without it, the 1.4.2 source falls back to a 1024-bit group built into nginx.

Apache splits by branch. The 2.4 changelog records mod_ssl gaining support for ECC keys and ECDH ciphers during 2.3 development, and 2.4.6, released this Monday, uses the prime256v1 curve for ECDHE. The 2.2.25 docs only describe RSA and Diffie-Hellman key exchange and its mod_ssl has no ECDH code, so on 2.2 DHE is your only forward secret option. Neither branch’s docs offer a way to set your own DH group, and both hand out a built-in 1024-bit group.

Turning it on

There are two parts to this. The cipher list puts ephemeral suites first, and the server has to be told to enforce that order, because by default the client’s preference wins. On nginx, ssl_prefer_server_ciphers defaults to off, and the default ssl_ciphers is HIGH:!aNULL:!MD5:

ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+HIGH:EDH+HIGH:HIGH:!aNULL:!MD5;
ssl_dhparam /etc/nginx/dhparam.pem;

Generate that file with openssl dhparam -out /etc/nginx/dhparam.pem 2048, or drop the line if the built-in 1024-bit group is acceptable to you. Joining EECDH and EDH with HIGH keeps out weaker suites those aliases would otherwise pull in, like null encryption, export, single DES and RC4. The plain HIGH after them adds the remaining strong suites, mostly RSA key exchange, at the end for older clients. OpenSSL’s cipher list rules say re-adding a suite that’s already in the list doesn’t move it.

On Apache 2.4, SSLHonorCipherOrder makes the server’s preference win (it’s been around since 2.1 with OpenSSL 0.9.7):

SSLHonorCipherOrder on
SSLCipherSuite EECDH+HIGH:EDH+HIGH:HIGH:!aNULL:!MD5

On 2.2, keep SSLHonorCipherOrder on and use EDH+HIGH:HIGH:!aNULL:!MD5 as the cipher string. Before reloading either server, run openssl ciphers -v with your string against the OpenSSL your server links. The entries marked Kx=ECDH and Kx=DH should come first.

Checking with openssl s_client

With OpenSSL 1.0.1, connect and look at the cipher line:

openssl s_client -connect example.com:443 -servername example.com < /dev/null | grep "Cipher is"

You want something like New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384. A name starting with ECDHE-, DHE- or EDH- (OpenSSL’s older spelling, as in EDH-RSA-DES-CBC3-SHA) means an ephemeral key exchange. A name with no key exchange prefix, like AES128-SHA or RC4-SHA, means plain RSA. With server ordering on, the result reflects your preference, since s_client offers a long list of suites. To confirm the DHE fallback for clients without elliptic curve support, add -cipher EDH. Check every virtual host, because each one can carry its own cipher settings.

Session resumption can undo it

Session tickets deserve a look. RFC 5077 tickets, in the recommended format, carry the session’s master secret encrypted under a server-side key, and the server sends the ticket before encryption starts. Anyone who later gets that ticket key can open recorded tickets and read those sessions, forward secret suite or not. The RFC recommends changing ticket keys regularly. Apache 2.4’s SSLSessionTicketKeyFile docs suggest single servers skip the file and rely on random keys generated at startup, and rotate the file frequently if you do use one. nginx 1.4 has no ticket key directive, so OpenSSL creates random keys when the configuration loads. Without a key file, a restart means new keys, so restarting now and then limits what a leaked ticket key exposes.

What forward secrecy doesn’t do

It protects recorded traffic from a key that leaks later. It does nothing for a session whose endpoint is already compromised when it happens. If someone controls your server while traffic is flowing, they can read requests right there, and an attacker in the middle who already holds your private key can impersonate your site to new visitors. Forward secrecy won’t stop malware on a laptop either. It narrows one specific risk, the recorded archive plus a future key, and that risk is exactly what makes it worth turning on.

The takeaway

Plain RSA key exchange makes your certificate key a master key for everything anyone ever captured. OpenSSL 1.0.0 or later, nginx 1.0.6 or later and Apache 2.4 all support ECDHE, and it’s a few lines of config: put EECDH and EDH suites first, make the server enforce its order, and confirm with s_client that you’re getting ECDHE- back.

← all posts