2.8 Testing Cipher Suite Preference
As a general rule, TLS servers should always be configured to enforce their cipher suite preferences, ensuring that they negotiate their preferred cipher suite with every client. This feature is essential with TLS 1.2 and earlier protocol revisions, which support many cipher suites, most of them undesirable. It’s a different story with TLS 1.3: it only has a handful of suites available at this time and all of them are secure, so enforcing server preference doesn’t matter that much.1
Because cipher suite preference doesn’t matter much with TLS 1.3, some stacks don’t even support it with this protocol, even if they do with earlier protocol versions. Thus, for the best results, you will want to test separately for TLS 1.3 and everything else—or separately for every supported protocol. This is another case in which automation is the better choice.
To test for server suite preference, you first need to have some idea of what suites are supported. For example, you could have the complete list of supported suites. Alternatively, you can probe the server with different suite types—for example, those that use ECDHE versus DHE or RSA key exchange.
With two suites in hand, you need to initiate two connections, first offering one of the suites as your first choice, then the other:
$ echo | openssl s_client -connect www.hardenize.com:443 -tls1_3 -ciphersuites 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384' 2>/dev/null | grep New
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
$ echo | openssl s_client -connect www.hardenize.com:443 -tls1_3 -ciphersuites 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256' 2>/dev/null | grep New
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
If you see the same suite negotiated on both connections, that means that the server is configured to actively select negotiated suites. Otherwise, it isn’t. The server in the previous example is one of those TLS 1.3 servers that doesn’t enforce preference. That very same server does have a preference with TLS 1.2; we can see that it always selects a better suite, even when we push it to the end of our list:
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher 'ECDHE+AESGCM RSA' 2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher 'ECDHE+AESGCM RSA +ECDHE' 2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256
When it comes to server suite preference testing, the ChaCha20 suites are best avoided. This is because some servers support another type of preference, where they treat AES-GCM and ChaCha20 suites as equal in terms of security and respect client preference as a special case. The idea is that the client will prefer the faster cipher suite, which is typically ChaCha20 for mobile devices and AES-GCM for desktops.
That said, with servers that support this type of preference, you may want to test if it’s working correctly. To do that, you’ll need to use three supported cipher suites and three tests. The purpose of the first two tests is to establish that the server selects its favorite suite when ChaCha20 is not involved:
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher 'ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-CHACHA20-POLY1305' 2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher 'ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-CHACHA20-POLY1305' 2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256
If you see that the server responds with the same suite in both cases, you can submit another test with a supported ChaCha20 suite first. If you see the server selecting it, you know it’s configured to support the client-preferred suite:
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher 'ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-GCM-SHA256' 2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305