Is the order of cipher names in TIdServerIOHandlerSSLOpenSSL.SSLOptions.CipherList
important?
Mostly Yes.
TLS does not specify who decides on the cipher. Conventionally, the server honors the client's preference. That is, the server will use the client's first preference if its available and enabled, or the client's second preference if its available and enabled, and so on.
Most libraries allow a server to override the behavior. For example, with OpenSSL, SSL_OP_CIPHER_SERVER_PREFERENCE
option. In this case, the server will match the server's first preference if the client advertises it, the server's second preference if the client advertises it, and so on.
For servers I control, I tune the cipher suite list and usually set SSL_OP_CIPHER_SERVER_PREFERENCE
because many clients don't pay attention to the details. They just throw the soup of cipher suites in the ClientHello hoping something sticks.
The order in which a server offers ciphers is important, but do I have control over this order?
Yes.
At the server, determine your order of cipher suites, and then set them with SSL_CTX_set_cipher_list
or SSL_set_cipher_list
. By setting the list, you ensure RC4-MD5
is not used even its the client's first choice (presuming you omit it). For maximum impact, also set SSL_OP_CIPHER_SERVER_PREFERENCE
context option.
Also see How do I disable a particular cipher suite in openssl? The context is OpenSSL, but it should give you an idea of where to go in Delphi.
(Comments): What's not clear to me from your answer: Does the CipherList property already signify an order? If I look at the underlying Delphi/Indy code I see that it is just a wrapper for external function SSL_CTX_set_cipher_list : function(_para1: PSSL_CTX; const str: PAnsiChar): TIdC_INT cdecl = nil; in IdSSLOpenSSLHeaders.pas
If I am understanding Delphi properly, then I believe the answer is Yes, it does. But its probably using either (1) OpenSSL's default list, or (2) a Delphi default list. Both are probably something like "ALL:!EXP:!LOW"
or similar. In either case, you will want to tune it to suite your taste.
If you are interested in what the list looks like when its put on the wire, use Wireshark to inspect the ClientHello. Its easy to generate one with s_client
, and it shows you what OpenSSL's default cipher suite list looks like:
$ openssl s_client -connect www.ietf.org:443 -tls1 -servername www.ietf.org
And the corresponding Wireshark trace with 55 default cipher suites stuffed into it:

You can also improve your security posture and reduce the number of cipher suites with the -cipher
option and "HIGH:!aNULL:!MD5:!RC4"
. The cipher suite count will be reduced to about 35.
$ openssl s_client -connect www.ietf.org:443 -tls1 -servername www.ietf.org \
-cipher 'HIGH:!aNULL:!MD5:!RC4'
If you don't like TripleDES and Cameilla, then you can reduce it to about 20 cipher suites with:
$ openssl s_client -connect www.ietf.org:443 -tls1 -servername www.ietf.org \
-cipher 'HIGH:!aNULL:!MD5:!RC4:!3DES:!CAMELLIA'
Now, suppose a client is configured with only RC4-MD5
and a server is configured with only AES-GCM
. I.e., there's no intersection of cipher suites between client and server. In this case, you will get an error in OpenSSL. The error will be 0x1408A0C1, "no shared cipher suites". It will look something like this at the server:
140339533272744:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1353
For OpenSSL-based clients and server, you can determine the default cipher suite list with the "DEFAULT" string. That's literally what its called (check the ciphers(1)
man page).
There's 103 of them, which includes weak and wounded algorithms. In practice, you want to pair it down to 16 or so cipher suites you feel comfortable using (i.e., your security posture):
$ openssl ciphers -v 'DEFAULT' | cut -f 1 -d " " | wc -l
103
And:
$ openssl ciphers -v 'DEFAULT' | cut -f 1 -d " "
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-SHA384
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES256-SHA
ECDHE-ECDSA-AES256-SHA
SRP-DSS-AES-256-CBC-SHA
SRP-RSA-AES-256-CBC-SHA
SRP-AES-256-CBC-SHA
DH-DSS-AES256-GCM-SHA384
DHE-DSS-AES256-GCM-SHA384
DH-RSA-AES256-GCM-SHA384
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES256-SHA256
DHE-DSS-AES256-SHA256
DH-RSA-AES256-SHA256
DH-DSS-AES256-SHA256
DHE-RSA-AES256-SHA
DHE-DSS-AES256-SHA
DH-RSA-AES256-SHA
DH-DSS-AES256-SHA
DHE-RSA-CAMELLIA256-SHA
DHE-DSS-CAMELLIA256-SHA
DH-RSA-CAMELLIA256-SHA
DH-DSS-CAMELLIA256-SHA
ECDH-RSA-AES256-GCM-SHA384
ECDH-ECDSA-AES256-GCM-SHA384
ECDH-RSA-AES256-SHA384
ECDH-ECDSA-AES256-SHA384
ECDH-RSA-AES256-SHA
ECDH-ECDSA-AES256-SHA
AES256-GCM-SHA384
AES256-SHA256
AES256-SHA
CAMELLIA256-SHA
PSK-AES256-CBC-SHA
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-SHA256
ECDHE-ECDSA-AES128-SHA256
ECDHE-RSA-AES128-SHA
ECDHE-ECDSA-AES128-SHA
SRP-DSS-AES-128-CBC-SHA
SRP-RSA-AES-128-CBC-SHA
SRP-AES-128-CBC-SHA
DH-DSS-AES128-GCM-SHA256
DHE-DSS-AES128-GCM-SHA256
DH-RSA-AES128-GCM-SHA256
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES128-SHA256
DHE-DSS-AES128-SHA256
DH-RSA-AES128-SHA256
DH-DSS-AES128-SHA256
DHE-RSA-AES128-SHA
DHE-DSS-AES128-SHA
DH-RSA-AES128-SHA
DH-DSS-AES128-SHA
DHE-RSA-SEED-SHA
DHE-DSS-SEED-SHA
DH-RSA-SEED-SHA
DH-DSS-SEED-SHA
DHE-RSA-CAMELLIA128-SHA
DHE-DSS-CAMELLIA128-SHA
DH-RSA-CAMELLIA128-SHA
DH-DSS-CAMELLIA128-SHA
ECDH-RSA-AES128-GCM-SHA256
ECDH-ECDSA-AES128-GCM-SHA256
ECDH-RSA-AES128-SHA256
ECDH-ECDSA-AES128-SHA256
ECDH-RSA-AES128-SHA
ECDH-ECDSA-AES128-SHA
AES128-GCM-SHA256
AES128-SHA256
AES128-SHA
SEED-SHA
CAMELLIA128-SHA
IDEA-CBC-SHA
PSK-AES128-CBC-SHA
ECDHE-RSA-RC4-SHA
ECDHE-ECDSA-RC4-SHA
ECDH-RSA-RC4-SHA
ECDH-ECDSA-RC4-SHA
RC4-SHA
RC4-MD5
PSK-RC4-SHA
ECDHE-RSA-DES-CBC3-SHA
ECDHE-ECDSA-DES-CBC3-SHA
SRP-DSS-3DES-EDE-CBC-SHA
SRP-RSA-3DES-EDE-CBC-SHA
SRP-3DES-EDE-CBC-SHA
EDH-RSA-DES-CBC3-SHA
EDH-DSS-DES-CBC3-SHA
DH-RSA-DES-CBC3-SHA
DH-DSS-DES-CBC3-SHA
ECDH-RSA-DES-CBC3-SHA
ECDH-ECDSA-DES-CBC3-SHA
DES-CBC3-SHA
PSK-3DES-EDE-CBC-SHA
EDH-RSA-DES-CBC-SHA
EDH-DSS-DES-CBC-SHA
DH-RSA-DES-CBC-SHA
DH-DSS-DES-CBC-SHA
DES-CBC-SHA