How to get A+ on the SSL Labs test in node.js

Actually, go read our next article because we just patched node to include these as default!

By Mike on 24th March 2015

Update May 18 2015: CertSimple has added newer, even better ciphers into node.js itself, which appears in node 4.

Once you've upgraded to node v4 - which you should, since it's the current stable - just add HSTS support per this article.

The SSL Labs test

The SSL Labs test examines a wide variety of aspects of HTTP servers, including simulating the handshakes where browsers and servers agree on crypto. It's a good starting point for checking your SSL configuration.

The test evolves over time - as new weaknesses get found in protocols, new technologies emerge that get added to requirements - so while the results here are accurate at publication, they might not be by the time you're reading this. Run your own tests and see.

Lastly: we're running this on node.js because a couple of the modules we use don't yet run on io.js. As you're about to see, io.js is a lot better out of the box than node 0.12 is. If you're using io.js, skip forward to the HSTS steps.

Introducing cipher suites

These are also called 'ciphersets' by node devs or just ciphers in the actual node function signatures.

Run the current stable node, 0.12. Start a TLS server. Statistically , it's likely you're using Express, so it will look something like this:

var server = https.createServer({
    key: privateKey,
    cert: certificate,
    ca: certificateAuthority
}, app);

The defaults for https.createServer() are described in the node docs. Expanding on that, you're actually running something like this.

var server = https.createServer({
    key: privateKey,
    cert: certificate,
    ca: certificateAuthority,
    ciphers: [
        "ECDHE-RSA-AES128-SHA256",
        "DHE-RSA-AES128-SHA256",
        "AES128-GCM-SHA256",
        "RC4",
        "HIGH",
        "!MD5",
        "!aNULL"
    ].join(':'),
}, app);

ciphers normally takes a string because that openssl expects, but we use an array and join it because it's a little easier to read, allows us to make comments above individual lines, etc.

Items in ciphers can take a few formats, including:

Now visit the SSL Labs test and test your server.

Out of the box, we've got some work to do.

Disabling RC4 and enforcing our preferred cipher order

OK, as SSL Labs notes, this server accepts the RC4 cipher, which is weak - there's more too, but we'll get to that later. Let's disable any cipher suite involving RC4 - using a ! just like the MD5 entry:

While we're at it, note this gem from the node docs:

honorCipherOrder : When choosing a cipher, use the server's preferences instead of the client preferences. Although, this option is disabled by default, it is recommended that you use this option in conjunction with the ciphers option to mitigate BEAST attacks.

It's somewhat worrying that the recommended option does not match the default. Let's use the recommended option:

// default node 0.12 ciphers with RC4 disabled
ciphers: [
    "ECDHE-RSA-AES128-SHA256",
    "DHE-RSA-AES128-SHA256",
    "AES128-GCM-SHA256",
    "!RC4", // RC4 be gone
    "HIGH",
    "!MD5",
    "!aNULL"
].join(':'),
honorCipherOrder: true

RC4 disabled, our grade is no longer capped at B. But something is broken with forward secrecy...

Fixing broken perfect forward secrecy

Perfect forward secrecy is rad. Even if your private key is stolen, the bad guys still need to get the session key of future conversations to read anything. That's amazing!

Since all crypto related items need cool logos now.

In the browser handshakes for Chrome 40 (which is already old at this point), Android 5.0.0 and Googlebot, you'll see 'No FS' in orange on the right. It turns out that Google product really seem to like AES128-GCM-SHA256 and picks it over the forward secrecy cipher suites (they start with an E for 'ephemeral'. This was found and fixed in io.js already.

Note the 'No FS' besides Google Chrome Desktop, Google Chrome on Android, and Googlebot

The same io.js pull request also sets honorCipherOrder to true by default.

Let's grab the ciphers from the iojs docs:

[
    "ECDHE-RSA-AES256-SHA384",
    "DHE-RSA-AES256-SHA384",
    "ECDHE-RSA-AES256-SHA256",
    "DHE-RSA-AES256-SHA256",
    "ECDHE-RSA-AES128-SHA256",
    "DHE-RSA-AES128-SHA256",
    "HIGH",
    "!aNULL",
    "!eNULL",
    "!EXPORT",
    "!DES",
    "!RC4",
    "!MD5",
    "!PSK",
    "!SRP",
    "!CAMELLIA"
].join(':'),

Using the iojs ciphers list fixes forward secrecy on Google products.

Finally (or firstly for io.js) using HSTS to tell browsers we expect to keep using HTTPS

For an A+ we need HSTS. HSTS tells clients using HTTPS to expect to keep using HTTPS for some time in future - which makes attacks that try and downgrade HTTPS to HTTP harder. For SSL Labs explicitly, the want at least six months ahead - which should be fine, since we plan to keep using HTTPS for a very long time.

Setting up HSTS only takes a moment. Grab the helmet module from npm. Assuming Express (because Express is popular), in app.js, it looks like:

var helmet = require('helmet');

var ONE_YEAR = 31536000000;
app.use(helmet.hsts({
    maxAge: ONE_YEAR,
    includeSubdomains: true,
    force: true
}));

And re test:

Boom.

Summary

node.js 0.12:

These issues have been resolved in io.js. Even if you can't use io.js yet, you can still use the io.js cipher suite settings to resolve them.

Both io.js and node.js 0.12:

Here's a node 0.12 default Express app modified to get A+ on the SSL Labs test.

Mike MacCana, founder at CertSimple.

CertSimple makes EV HTTPS fast and painless.

An EV HTTPS certificate verifies the company behind your website. But getting verified is a slow painful process. CertSimple provides EV HTTPS certificates 40x faster than other vendors. We check your company registration, network details, physical address and flag common errors before you pay us, provide verification steps specific for your company, update in realtime during the process, and even check your infrastructure to help you set up HTTPS securely.
Verify your site now!