FREAK and the Export Ciphers Still Hiding in Your Server Config
A week ago today, on March 3, researchers went public with a TLS attack called FREAK, short for Factoring RSA Export Keys. CERT/CC credits researchers from INRIA, Microsoft Research and IMDEA. The idea is that a man in the middle can talk certain TLS clients into using a 512-bit RSA key, which is small enough to factor, and then read the session. The bugs live in client code: OpenSSL, Apple’s Secure Transport, Microsoft’s Schannel and a few others. But the attack only works against servers that are still willing to use export-grade RSA cipher suites, and whether yours is comes down to one line in your config.
Why 1990s export crypto is still in TLS
In the 1990s the US restricted the export of strong cryptography, and TLS was designed around that. RFC 2246, the TLS 1.0 spec from January 1999, even carries a note that under then-current US export law, RSA moduli larger than 512 bits couldn’t be used for key exchange in software exported from the US. The protocol’s answer was the RSA_EXPORT key exchange. A server keeps its normal, larger certificate key, but when an export suite is negotiated it sends a ServerKeyExchange message with a temporary RSA key of 512 bits or less, signed by the certificate key. The client encrypts the premaster secret to that short key. Export suites also cap the bulk cipher at 40 bits, which is where names like TLS_RSA_EXPORT_WITH_RC4_40_MD5 come from.
That same section of the spec is strict about when the message is allowed. For plain RSA key exchange, sending a server key exchange message is simply not legal. A client that negotiated a normal RSA suite should never see a temporary RSA key at all. Hold on to that, because it’s the whole bug.
The export rules loosened, and in April 2006 RFC 4346 (TLS 1.1) called these suites unacceptably weak and said TLS 1.1 implementations must not negotiate them in TLS 1.1 mode. It still allowed clients to offer them for backward compatibility with TLS 1.0 and SSL 3.0 servers, though, so the code stayed. OpenSSL 1.0.1 still documents EXP and EXPORT as cipher aliases, and its built-in default cipher list is ALL:!aNULL:!eNULL:!SSLv2. Since ALL means every suite except the null encryption ones, export suites are in that default unless something takes them out.
What the attack needs
FREAK needs three things at the same time. The attacker has to be a man in the middle who can change handshake messages. The client has to be running one of the buggy TLS stacks. And the server has to be willing to negotiate an export RSA suite. Apple’s advisory says the problem only affected connections to servers that support export-strength RSA suites, and Microsoft lists the same thing as a mitigating factor.
Here’s roughly how it plays out. The client asks for normal RSA suites. The attacker changes that request to ask for an export RSA suite instead. The server, still happy to do export, replies with its certificate and a 512-bit temporary key signed by its real certificate key, so the signature checks out. The attacker passes that along with the reply dressed up as the normal RSA suite the client asked for. A correct client would reject a temporary RSA key showing up in a plain RSA handshake. A vulnerable one uses it, and encrypts the premaster secret to a key the attacker can factor. The CERT/CC vulnerability note sums up the result: factor the weak key, recover what’s needed to build the session keys, and decrypt the session. Red Hat’s security team wrote on March 4 that factoring a 512-bit key is doable with today’s cloud and GPU infrastructure, and that the attack allows both decryption and alteration of the session.
Servers also don’t mint a fresh export key per connection. RFC 4346 explicitly allows one temporary RSA key for many sessions, and in the source of nginx 1.7.10 and Apache 2.2.29 the 512-bit key is generated once and then handed to every export handshake until the process restarts. My read is that one factoring job against a busy server can pay off across a lot of connections.
Which clients were affected
The OpenSSL side is CVE-2015-0204, and it isn’t new. The OpenSSL advisory from January 8 says an OpenSSL client will accept an RSA temporary key in a non-export RSA suite, affecting 1.0.1, 1.0.0 and 0.9.8, fixed in 1.0.1k, 1.0.0p and 0.9.8zd. Karthikeyan Bhargavan of the PROSECCO team at INRIA reported it on October 22, 2014, and OpenSSL rated it low severity at the time. The fix, per the 1.0.1k changelog, removes the non-export ephemeral RSA code from both client and server. Distributions picked it up in January too: Ubuntu in USN-2459-1 on January 12, and Red Hat for RHEL 6 and 7 in RHSA-2015:0066 on January 20. Distro packages backport fixes, so the patched RHEL 6 package is still called 1.0.1e. Look for the CVE in the package changelog, not the version string.
Apple’s bug is CVE-2015-1067 in Secure Transport, Apple’s TLS library, and CERT/CC lists Safari as affected. It was fixed yesterday, March 9, in iOS 8.2 and in Security Update 2015-002 for OS X Mountain Lion 10.8.5, Mavericks 10.9.5 and Yosemite 10.10.2. Apple’s fix removes support for ephemeral RSA keys entirely.
Microsoft’s is CVE-2015-1637 in Schannel, affecting all supported versions of Windows. Security Advisory 3046015 came out on March 5 with only a workaround, reordering cipher suites through Group Policy, and today’s bulletin MS15-031 ships the actual patch. Microsoft describes the flaw as a client accepting an RSA key shorter than the one originally negotiated.
The CERT/CC note also lists Google’s BoringSSL before November 10, 2014, Chrome before version 41 and the Android browser, Opera before 28 on OS X and Android, and the BlackBerry browser as affected. GnuTLS, Botan, cryptlib and Bouncy Castle are listed as not affected.
The vulnerable clients are your visitors’ phones and laptops and whatever HTTP libraries your API consumers run, and you can’t patch any of those. What you can do is take away the third ingredient.
Making sure nginx offers no export suites
In nginx the directive is ssl_ciphers, valid in http and server blocks. The nginx docs give the default as HIGH:!aNULL:!MD5, which has been the default since 1.0.5, and export suites never count as HIGH in OpenSSL, so if you never set ssl_ciphers you’re fine.
There are two traps, though. nginx 0.7.64, 0.8.18 and earlier defaulted to ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP, which includes export suites. And the example string in the ssl_ciphers documentation itself still ends in +HIGH:+MEDIUM:+LOW:+SSLv2:+EXP. In OpenSSL cipher strings a + only moves matching suites to the end of the list, it doesn’t remove them, so anyone who copied that example is offering export suites to anyone who asks. Grep your whole config for ssl_ciphers, since every server block can set its own, and add an explicit exclusion so there’s no doubt.
ssl_ciphers HIGH:!aNULL:!MD5:!EXP;
OpenSSL’s docs say suites removed with ! can never reappear, even if the string names them later. On the default string !EXP changes nothing, but it protects you from the next person who edits the line. Reload nginx afterwards.
Making sure Apache offers no export suites
In Apache httpd the directive is SSLCipherSuite, and it can be set in server config, virtual host, directory and .htaccess context. If you’re on 2.4.7 or newer, the 2.4 documentation says mod_ssl always disables null and export ciphers by prepending !aNULL:!eNULL:!EXP: to whatever you configure, so there’s nothing you can get wrong here.
Older versions don’t have that safety net. The 2.2 docs list the default as ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP, so if nothing in your config sets SSLCipherSuite, you’re offering export suites. The sample httpd-ssl.conf in 2.2.29 sets HIGH:MEDIUM:!aNULL:!MD5, which has none, but distributions ship their own SSL config. Check every virtual host and add the exclusion explicitly.
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!EXP
Then reload Apache. If you run IIS, Microsoft says export RSA suites are disabled in the default configuration of Windows Vista and Server 2008 and later, so only a server someone has customized, or an older Windows Server 2003 box, is likely to be offering them.
Checking your own server
Start by checking what your cipher string actually expands to, using the openssl ciphers command on the server itself, since the result depends on the OpenSSL version installed there. In verbose mode, export suites show up with export at the end of the line.
openssl ciphers -v 'HIGH:MEDIUM:!aNULL:!MD5:!EXP' | grep export
Paste in your real string. That command should print nothing.
Then test the live server from the outside, offering it nothing but export suites.
openssl s_client -connect example.com:443 -cipher EXP
If the server has no export suites enabled, there’s nothing to agree on, so the handshake fails and the session summary shows Cipher is (NONE). If the handshake completes and you see a cipher name starting with EXP-, like EXP-RC4-MD5 or EXP-DES-CBC-SHA, you’re still offering export suites somewhere. Two gotchas. If openssl ciphers -v EXP on your own machine gives an error instead of a list, your local OpenSSL has no export suites and the test tells you nothing. And OpenSSL 1.0.1’s s_client doesn’t send SNI unless you ask, so if several HTTPS sites share an IP address, add -servername example.com to make sure you’re testing the virtual host you think you are. Run it against every hostname and port you serve TLS on, not just the main site.
The takeaway
Export-grade crypto was a 1990s legal compromise that outlived the law behind it, and FREAK is what happens when client libraries get sloppy about when those old keys may show up. Client fixes are rolling out, but you don’t control when visitors install them. What you do control is whether your server will hand a 512-bit key to anyone who asks. Put !EXP in ssl_ciphers or SSLCipherSuite, reload, and prove it with s_client against every endpoint.