50 comments

[ 2.8 ms ] story [ 129 ms ] thread
Hi there, author here.

This began as an exercise in improving the rating for an individual website, the iojs advocacy just came as a side effect of investigating what was needed and running into the megapatch in iojs (https://github.com/iojs/io.js/pull/826) where they fixed the tls defaults affecting node 0.12's out of the box score.

Let me know if you have any additions or corrections, there's also an express boilerplate app used to pass the tests here: https://github.com/mikemaccana/ssltest

Isn't this all better implemented in the nginx/apache server in front of the application?

I've just done a very similar thing for my Django hosted website, but didn't touch Python at all for it.

Edit: Here's a blog post where I detail the steps to set this up in Nginx - https://news.ycombinator.com/item?id=9256200

Sometimes you're running a single application via AWS OpsWorks or so, and your application is handling the SSL because you don't want to pay for an elastic load balancer to handle the SSL.
Fair enough, I'm not familiar with that service.

Running a Node.js application without a reverse proxy in front of it sounds like poor practice though, particularly from a security standpoint.

Do you have any particular security holes in mind that nginx guards which node.js / io.js are open for ?
Not off the top of my head, although I know the recommendation at least used to be that Node was not run with full public access.

I think it makes sense to separate the security and low level details of serving a public site, and the details of hosting an application though. This is common practice with Django, using gunicorn and nginx, and I believe with Ruby as well in a similar manner.

Django and Ruby /can't/ serve static content at any speed as they block their main thread. Node and nginx use the same model for io, as does Tornado and Eventmachine (though the latter two are nowhere near as popular).
Separating your TLS server from your app server also shields your app server from leaks in your TLS server. See: Heartbleed.
True, however the higher the number of different software packages that participate in your application's operational environment, the higher the chances that one of these exposes security holes. You have to make a trade-off between complexity and increasing security by isolating pieces of your stack.
You can at least configure some basic sanitizations, so you'r app doesn't have to deal with them. Examples of the top of my head, are normalizing slashes and removing invalid headers.

Whether that's worth it is another question.

Edit: added reply to 'vkjv' below as I have hit the comment threshhold and have been unable to reply to this account. Since HN seems to be somewhat unsure of node's concept of IO (why I imagine the downmods) and why it's significantly different to common Python and Ruby setup (Tornado and EventMachine excepted).

The main point of nginx is event based, non-blocking IO. That's why traditionally blocking languages (like your Python setup) use nginx for static content and their own engines for dynamic stuff.

However event based, non-blocking IO is also the main point of node. So most people using, say Express would use the express module's inbuilt 'static' middleware rather than add an entire webserver to replicate the built-in functionality.

Unless there's a compelling reason to do SSL elsewhere (for example, an AWS ELB would allow you to isolate & consolidate your SSL in a separate layer) then you'd want to avoid adding complexity.

Edit: reply to vkjv:

> it still needs to serialize that data from I/O to the request and that is both blocking and slow.

No. If the socket wasn't ready, node wouldn't block writing data there. streams are evented and that includes sockets. Hence the callback on socket.write() https://nodejs.org/api/net.html#net_socket_write_data_encodi...

    var socket = new net.Socket();
    socket.write(...)
    console.log('Yep sockets are non blocking too')
This is why both node and nginx have outperformed each other in different tests.

Agreed, if you're already using nginx for other reasons, eg, load balancing, you should use nginx for SSL. If you're not, think carefully before doubling the size of your stack without specific reason to do so.

I just recently was in this same situation (deciding whether to serve static via express' static middleware or nginx), and nginx proved to be a better choice - the response (on a non-loaded server) was faster, and less server CPU time was used up.

Wasn't a scientific test, but the results were consistent enough for us to decide to use nginx.

(FWIW, neither nginx nor node did SSL termination - we have haproxy in front of them taking care of that and the load balancing itself).

Back in November I spent a lot of time trying to get an A+ for my site throwpass.com. I couldn't get forward secrecy to work and found several places saying node.js doesn't support it. So I ended up forwarding through nginx.
(comment deleted)
Makes sense. This is likely the same PFS issue covered in the article.
While node.js is great at non-blocking IO, it's not great at the blocking kind. E.g., shuffling bytes.

While you are correct that node.js is happily not blocking the main thread while reading from disk, once that returns it still needs to serialize that data from I/O to the request and that is both blocking and slow.

I highly advise that you don't use node.js to service static content outside of a development environment. And if you are already using nginx in front of node, you might as well use it for SSL.

As a bonus, nginx does a great job proxying multiple node.js processes. You could also use the cluster module, but last time I checked, that was still marked as experimental.

> it still needs to serialize that data from I/O to the request and that is both blocking and slow.

That's incorrect. See edit to parent post (sorry I couldn't reply earlier).

I know that the parent replied already, but I want add that using node for static content is a good idea. I/O is what Node excels at. As long as you use the non-blocking API to load content, you should get good throughput. What Node is bad at is processing dynamic things. If your templates are CPU intensive, you'll block Node.

This is why I'm, to this day, confused about building whole websites on Node. Dynamic, templated web sites are computation intensive (without result caching). Sure you can have N-Nodes running for N cores. This will minimize blocking. I/O for DB is time intensive so Node will pickup other workloads while it waits. So everything averages out, but it still an interesting thing often overlooked.

Yes, I think is better to put nginx in front to handle this and sooner or later you will need some kind of reverse proxy as soon as your application grows. The reasons might be load balancing or just to map components of your app like "app.com" and "app.com/api" being different applications.

One big difference to note is that nginx link to the local openssl while node binaries embeds an openssl. If there is a bug in openssl I can apt-get update/upgrade and I get the patch very early.

Personally I think nginx is awesome. They are going to support JavaScript soon, but I have done things with LUA and is great too.

> They are going to support JavaScript soon

Whoa. Slow down. They have only confirmed replacing their custom configuration language with JavaScript and hinted at an official JavaScript module [1], as opposed to the existing third-party module [2].

Bundling a Node.js-like JavaScript api layer with their official module would be a logical next step, because nginx is an event loop anyway. But, I highly doubt JavaScript will be anywhere near the Nginx core.

[1] http://www.infoworld.com/article/2838008/javascript/nginx-ha...

[2] https://code.google.com/p/ngxv8/

Thanks for the clarification, that's what I though but my comment was quite vague
I kind of wish they wouldn't just hand out an A+ that easily. There are clear areas of improvement that the sub-scores call out (90 in both Key Exchange and Cipher Suite). They see the HSTS and grant the A+ when there is clearly improvement that can be made. In my mind, an A+ should be reserved for an SSL/TLS implementation that cannot reasonably be improved upon given the current state of the industry.

To improve your Key Exchange score you can bump your certificate from a 2048-bit key up to 4096-bit (giving you a score of 100). That's probably a good idea anyway as 2048 has been the minimum safe key size since 2010 [1], and I'd imagine that 4096 will become the new minimum over the next few years.

To improve your Cipher Suite score you can easily drop support for payload encryption with keys less than 256 bits in length. You will risk not supporting IE 6 users, but that's a fringe case these days (and there are many more issues with supporting IE 6 on top of that).

Making those adjustments, you should be able to get your scores to 100,95,100,100 which, in my opinion, is much more worthy of an A+.

There may be a few other tweaks you can make to improve items in the Protocol Details section, but I don't know enough about the node.js/io.js system to be able to recommend a clear path for improvement there.

The only score that you really would have difficulty bumping to 100 is the Protocol Support which is limited by the fact that all versions of IE prior to 11 had TLS 1.1 and 1.2 disabled by default. Unfortunately, there are still many IE 9 and 10 users out there

1. http://csrc.nist.gov/publications/nistpubs/800-131A/sp800-13...

Thanks & agreed completely. I'm working on a followup improving the cipher suite score (with a couple of options depending on browser compromises).

I'll do some research around 4096 bit key support. I'm also checking out OCSP stapling.

Edit: precursory investigations show 4096 bit keys generally good to go with a few exceptions, eg AWS CloudFront:http://docs.aws.amazon.com/AmazonCloudFront/latest/Developer...

> I'd imagine that 4096 will become the new minimum over the next few years.

That's unlikely to happen. 2048-bit RSA provides a security level of ~112 bits, which would physically require a massive amount of energy to break, and it's unlikely that so much energy would ever be expended on a single task. So 2048-bit RSA is only likely to be broken if there's a mathematical breakthrough against RSA. The thing is, such a breakthrough is likely to break 4096-bit RSA as well, so using 4096-bit RSA is only a protection against a minor breakthrough.

Meanwhile, 4096-bit RSA carries a massive performance cost. It's just not worth slowing down your TLS handshake with 4096-bit RSA to protect against a mathematical breakthrough, but not too much of a breakthrough.

So stick with 2048-bit RSA for now, and in a few years when elliptic curve crypto is better supported, switch to that.

Totally This ^

This is a key you get re-signed every 1-3 years. You don't need it to last 10 years, you need it to last maybe a year longer than your SSL cert is actually good for. (Assuming your site is setup to have all the browsers do PFS)

The explanation of the Full cipher suite format has some tiny mistakes for the explained case.

> Use GCM for the cipher mode > Use SHA256 for message authentication (making sure messages haven't been tampered with)

GCM is an AEAD-Mode, it builts upon CTR-Mode, but also provides message authentication. SHA256 is used as a PRF (Pseudo Random Function) to generate key material based on the master secrect. See https://tools.ietf.org/html/rfc5246 Section 5 for more information.

Thank you! I've published a correction in the article and credited you.
Why people are that "concerned" about getting A+ on SSL Labs...
I'm not really sure what you're asking about or commenting on here. I will, however, take the opportunity to say that I am a big fan of the SLL Labs Server Test that Ivan Ristic made. With the number of SSL/TLS vulnerabilities over the past few years, it's important to ensure that secure servers stay current with best practices. This tool offers a free way to audit that.

Getting an A+ doesn't guarantee security as your application and infrastructure must all be kept current and secured. The A+, however, is intended as an indicator that your SSL/TLS is configured to provide the optimal level of security around the transport layer. If a site gets a grade of B, that doesn't necessarily make it insecure, but it does indicate that certain vulnerabilities could expose their users' data.

The problem by getting an A+ is that you are probably blocking a lot of clients from reaching your service.
No, that's not true. The only client with non-negligible use that you have to lock out to get an A+ is IE6 (because IE6 only supports SSLv3, at best, by default).
IE6 Must Die. Except for China, IE6 is used by less than 1% of the population around the world, according to Microsoft's own data:

https://www.modern.ie/en-us/ie6countdown

Indeed. Locking IE6 out to get an A/A+ on SSL Labs is totally worth it.
Absolutely. If people have old hardware or OS's, they could at least switch to an older version of a better browser.
I did split ioerror's duraconf nginx configs in 3 distinct configs with different ciphersuites lists, that specifically list desired ciphers in order, targetting :

- Very high security with low compatibility.

- PFS-only moderate security with high compatibility.

- PFS-centric moderate security with very high compatibility.

All of them give an A on Qualys SSL Labs test.

https://github.com/ouaibe/duraconf/tree/master/configs/nginx

Is it rude that SSL Labs publishes those who got Fs?
It publishes all of them, I thought, unless you select the "do not publish my results" box on the main screen.
There's a "don't publish" checkbox on the main page right below where your URL is entered. Also, anecdotally, most of the F's I see are from untrusted certs. (I.e. the encryption is fine, the site in question just didn't pay the CA protection money.)
This isn't prioritizing quite the right ciphers. The CBC mode construction in TLS has serious problems (MAC-then-encrypt instead of encrypt-then-MAC) and should be considered cryptographically broken. It's incredibly fragile and difficult to implement correctly.

You want to make sure TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 is negotiated, at least where browsers support it. (It's spelled "ECDHE-RSA-AES128-GCM-SHA256" in OpenSSL.) There's a small handful of others that are also acceptable, but between ECDSA certificates being rare and CHACHA20_POLY1305 still being standardized, that's the one you want. All the rest are legacy baggage.

Mozilla has some server-side recommendations here: https://wiki.mozilla.org/Security/Server_Side_TLS

Just out of curiosity, why are you proposing that AES 128 is the one to go with? According to SSL Labs AES 256 is preferable and has broad support among browsers.
SSL Labs is judging what they consider absolute security (I assume - I can't speak for Ivan or Qualys), where Google/Mozilla/etc look heavily at performance and supportability. Mozilla publishes their approach to what is 'best' here: https://wiki.mozilla.org/Security/Server_Side_TLS