Skip to main content
1 of 4

First, let's go over how cipher suite negotiation works, very briefly. For example, we can use the TLS 1.2 document RFC 5246 starting at section 7.4.1.2 to see, in the short short form:

  • ClientHello: The client tells the server which cipher suites the client supports
  • Now the server picks one
  • I'll discuss how to control which one it picks next!
  • ServerHello: The server tells the client which cipher suite it has chosen, or gives the client a failure message.

Now, as to the actual selection. I've used the nginx ssl module documentation, the Qualys 2013 article on Configuring Apache, Nginx, and OpenSSL for Forward Secrecy, and the Hynek Hardening Your Web Server’s SSL Ciphers article for reference. The latter two cover both Apache and Nginx (as both use OpenSSL as a base).

Essentially, you need to tell Nginx to use the order you select, and you need to select an order. To see what the results of that order would be, you can use the OpenSSL command line, i.e.

openssl ciphers -v EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED

NOTE: You may want to remove :!3DES from that string; 3-key triple-DES isn't efficient, but it is still secure in and of itself to more or less 112 bits of security, and is very, very common.

Use the above command to determine which cipher suites will be most preferred and least preferred in your configuration, and change it until you like the results. The references I've given have their own strings; I amended it slightly to get the above example (removing RC4 and SEED, and putting every TLS 1.2 cipher suite above any 'SSLv3' cipher suite, for example).

Then, for Nginx in particular, you would alter your configuration file to include something like:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED";

Add in SSLv3 to ssl_protocols if you really insist on it.

The ssl_prefer_server_ciphers will inform nginx to use the order we specify, and ignore the order the client presents their cipher list in. Now, if the only shared cipher suite between the ClientHello and the list OpenSSL ciphers -v ... gives is our least preferred cipher, that's of course what nginx will use. If nothing matches, then we send the client a failure notice.

The ssl_ciphers command is the meat of the choice, here, as nginx will inform OpenSSL of our preferred cipher suite list. Please, please use the openssl ciphers -v command to see the results you get on your platform. Ideally, check it again after changing OpenSSL versions.

Also, please read Scott Helme's article on Setting up HSTS (HTTP Strict Transport Security) in nginx, which will allows a host to enforce the use of HTTPS on the client side. Be sure to include the HSTS header inside the http block with the ssl listen statement.