I'm currently using nginx with the following ciphers:

ssl_ciphers HIGH:!aNULL:!eNULL:!LOW:!ADH:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;

I would like to maintain compatibility to older browsers, especially also older mobile browsers and therefore not completely disallow SHA1.

How can I achieve that SHA256 is preferred over SHA1 for MAC (Message Authentication Code ) and always used when possible.

I can i.e. force SHA256 to be applied by adding SHA256:!SHA: to my ssl_ciphers string but this would also disallow SHA1 completely.

With the ssl_cipher at the beginning it tends however to just use SHA1. Any recommendations?


Update 29.12.2014

Thanks everybody for the constructive inputs and discussion.

Even though I still think that the Mozilla page on Server side TLS overall covers the topic quite good - I would only recommend the Modern compatibility with the limitation that the DSS ciphers should be removed from it and explicitly disallowed (!DSS) as recommended in the comment by Anti-weakpasswords - thanks for spotting it.

ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK

Interestingly ssllabs did not alert or down rate for this...

Further I prefer to use custom generated diffie hellman parameters. Even though the standard ones are obviously considered safe. What are the OpenSSL standard Diffie Hellmann parameters (primes)?

openssl dhparam -check -out /etc/ssl/private/dhparams.pem 2048

increase that to 4096 for paranoia and fun if you like.

  • 1
    Re update4: the full cipherstrings are not in the three examples at #Nginx, but they are near the top of the page, and somes conversions to not-(wholly-)OpenSSL forms near the bottom. The generator page covers a narrower range, but is convenient within that range. – dave_thompson_085 Dec 15 '14 at 9:20
  • True, didn't notice that. Thanks for pointing it out. – binaryanomaly Dec 16 '14 at 21:21
  • 2
    Re: Update 16.12.2014: I would strongly suggest at minimum removing the -DSS- ciphers and adding !DSS to that string; DSS ciphers tend to be limited to too-small 1024 bit key sizes. See https://www.ssllabs.com/ssltest/viewClient.html?name=IE&version=11&platform=Win%208.1, cipher suite list, superscript note 2, "(2) Cannot be used for Forward Secrecy because they require DSA keys, which are effectively limited to 1024 bits." – Anti-weakpasswords Dec 28 '14 at 20:05
  • Thanks for the sharp eyes! No idea why Mozilla added DSS it was even not included in the standard nginx setting. Interestingly ssllabs checks didn't complain or rate it down. I'll update the post. – binaryanomaly Dec 29 '14 at 18:49
up vote 25 down vote accepted

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, e.g.

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.

Edited to add: At least after this (if not before also), go to Qualys SSL Labs to see HTTPS security information and to Test Your Server that's been kept pretty well up to date for the last few years. Recommendations change regularly, and sometimes even frequently reverse themselves (RC4, for example, what nearly whiplash inducing). You can also even Test Your Browser!

  • Thanks openssl -v ciphers ... is exatcly what I needed. – binaryanomaly Apr 2 '14 at 17:40
  • You're welcome; see also the Qualys SSL Labs in the new last paragraph. I'm curious - why forbid Camellia? It's an equivalent block cipher to AES, officially endorsed by the EU and Japan, per the results of the NESSIE and CRYPTREC competitions, respectively. – Anti-weakpasswords Apr 3 '14 at 6:11
  • Good point, I just copied that from the cloudlfare ciphers. I see no additional benefit though when allowing it and retesting on cloudflare. It seems not to be FS capable? I'm really not a cipher algorithm expert though. – binaryanomaly Apr 3 '14 at 7:20
  • 1
    Forward secrecy is an attribute of having an ephemeral key in the mix (the last "E" in "DHE" or "ECDHE", for example); it has nothing to do with the choice of symmetric cipher as such. Quite a lot of the ciphers my OpenSSL install shows for your currently chosen list don't have forward secrecy, like AES128-GCM-SHA256 (third most preferred on your list). ECDHE-RSA-AES128-GCM-SHA256, of course, does offer forward secrecy. – Anti-weakpasswords Apr 3 '14 at 7:34
  • 2
    @binaryanomaly TLS 1.2 includes the full set of modern Camellia cipher suites, PFS and otherwise. Unfortunately, OpenSSL doesn't. OpenSSL does support the older ones like DHE-RSA-CAMELLIA128-SHA, but not the newer ECDHE or ECDSA ones. – Matt Nordhoff Apr 4 '14 at 1:02

Mozilla has an online tool that will help you choose the correct cipher suite.

https://mozilla.github.io/server-side-tls/ssl-config-generator/

It will let you input your server version, software version, etc. and then choose between a balance of security and legacy support.

  • Read the question including the update again and you'll see that this information is already contained. Further Mozilla recommendation is soso. – binaryanomaly Apr 22 '15 at 10:39
  • @binaranomaly I read the update. It links to the mozilla wiki but my understanding is that the tool I linked to is better because it is kept up to date by taking your server version and other parameters as input – Alex W Apr 22 '15 at 12:36
  • 2
    very nice tool without need to read all the pitfalls, you only need to know what are you using – tyoc213 Nov 26 '15 at 23:34

OpenSSL naturally will prefer newer MACs for otherwise-equivalent cipher suites. For example, the lengthy openssl ciphers -v output for your cipher string starts with:

ECDHE-RSA-AES256-GCM-SHA384    TLSv1.2  Kx=ECDH        Au=RSA    Enc=AESGCM(256)    Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384  TLSv1.2  Kx=ECDH        Au=ECDSA  Enc=AESGCM(256)    Mac=AEAD
ECDHE-RSA-AES256-SHA384        TLSv1.2  Kx=ECDH        Au=RSA    Enc=AES(256)       Mac=SHA384
ECDHE-ECDSA-AES256-SHA384      TLSv1.2  Kx=ECDH        Au=ECDSA  Enc=AES(256)       Mac=SHA384
ECDHE-RSA-AES256-SHA           SSLv3    Kx=ECDH        Au=RSA    Enc=AES(256)       Mac=SHA1
ECDHE-ECDSA-AES256-SHA         SSLv3    Kx=ECDH        Au=ECDSA  Enc=AES(256)       Mac=SHA1

Of course, TLS will only use cipher suites supported mutually by both the server and client, and neither Chrome nor Firefox support HMAC-SHA256 cipher suites. Since HMAC-SHA1 (and even HMAC-MD5) are still considered secure, I believe their developers (and those of NSS, the TLS library they both use) are skeptical of wasting developer effort and TLS handshake size adding new, unnecessary, and backwards-incompatible cipher suites.

Look at, for example, Chrome 33's supported cipher suites in order of preference:

  1. ChaCha20-Poly1305,
  2. AES-128-GCM,
  3. AES-256-CBC with HMAC-SHA1,
  4. RC4 (ugh) and AES-128-CBC with HMAC-SHA1, ...

OpenSSL doesn't support ChaCha20-Poly1305. If yours doesn't support AES-GCM either (???) and uses an RSA certificate, ECDHE-RSA-AES256-SHA is naturally the cipher suite Chrome will use. (Firefox 29 would use ECDHE-RSA-AES128-SHA.)

(The "SHA-256" cipher suites you've seen being used on other websites are presumably ChaCha20-Poly1305 or AES-128-GCM, which are AEADs that do not make use of HMAC, but whose cipher suites use SHA-256 in the PRF.)

for best compatibility the cloudflare-cipher-suite is not the best; i found the following better:

# suggestion from sslabs / including PFS, good compatibility
#ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;


# suggestion my mozilla-server-team - good compatibility, pfs
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;

when you check each setup with ssllabs you'll find for those 2 suites the following statement:

  • Forward Secrecy Yes (with most browsers) ROBUST

while on your suggested cipher_suite it says:

  • Forward Secrecy Yes (with modern browsers)

additional information: Guide to Nginx + SSL + SPDY

  • Thanks for the info. The mozilla recommendation looks best to me, less RC4 use with that ciphers. – binaryanomaly Apr 4 '14 at 19:18
  • 1
    For what it's worth, Mozilla offers a second configuration string farther down the page, in the "RC4 Weaknesses" section, that doesn't use RC4 at all. For IE/XP compatibility it instead uses 3DES, which is slow but actually more or less secure. – Matt Nordhoff Apr 5 '14 at 4:59
  • @MattNordhoff: thanxs for that addition; from the docs: "While 3DES provides more resistant cryptography, it is also 30 times slower and more cpu intensive than RC4." i must say, <rant>, i'd give a sh*t on someones security, if he/she is using a browser like ie 6/7, if this leads to a massive impact on performance </rant> – that guy from over there Apr 5 '14 at 9:30
  • In the mozilla ciphers the only thing the RC4 strings are for is for IE8/XP support according to the output of the qualys test. Unfortunately still a far too common (corporate) setup. In that case RC4 can be an ok compromise as IE8/XP is performance wise already a pain and my site is not security critical at all. – binaryanomaly Apr 6 '14 at 22:04

Choice of hash function (SHA-1 vs SHA-256) does not really depend on the cipher suite, but on the protocol version. Basically, you get SHA-256 if you use TLS 1.2, SHA-1 if you use an older version.

(Yes, I known this is a simplified description of a slightly more complex situation, but here it works.)

Normally, client and server will use the highest protocol version that they both support, so you should already get TLS-1.2 (hence SHA-256) whenever it is possible. Note that this requires that both the server's code (OpenSSL) and the client's code are recent. You can alter that mechanism explicitly with ssl_protocols.

  • Well server side I have ssl_protocols TLSv1 TLSv1.1 TLSv1.2; And I use Firefox 29 which should be TLS 1.2 capable, so why do I end up with SHA1? – binaryanomaly Apr 1 '14 at 18:42
  • I might have to add that on other sites I am able to use SHA256 so it cannot the browser? – binaryanomaly Apr 1 '14 at 18:48
  • Chrome actually says it's TLS 1.2 with SHA1? The connection uses TLS 1.2. The connection is encrypted using AES_256_CBC, with SHA1 for message authentication and ECDHE_RSA as the key exchange mechanism. – binaryanomaly Apr 1 '14 at 18:52
  • 1
    Sorry, but no. The PRF hash depends on the version, but the HMAC hash (still) depends on the suite. To be exact, rfc5246 specifies the PRF for all pre-existing suites (now) uses SHA-256, and the PRF for new suites is defined per suite (as well as HMAC if used), and all new suites to date define SHA-256 or SHA-384. It would be possible to define a new suite using PRF (and/or HMAC) SHA-1 or even MD5, but it would be crazy; what will probably make sense soon are some suites using SHA-3. – dave_thompson_085 Dec 30 '14 at 9:46

Comodo recommendation is:

ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

Source: https://support.comodo.com/index.php?/Default/Knowledgebase/Article/View/789/37/

https://cipherli.st/ is another site that offers:

The above ciphers are Copy Pastable in your nginx, Lighttpd or Apache config. These provide Strong SSL Security for all modern browsers, plus you get an A+ on the SSL Labs Test.

This stuff goes out of date all the time, but for what it's worth, in May 2018 here's its recommendations for nginx:

ssl_protocols TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2
ssl_prefer_server_ciphers on; 
ssl_dhparam /etc/nginx/dhparam.pem; # openssl dhparam -out /etc/nginx/dhparam.pem 4096
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout  10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.