Securing TLS

A StackExchange question on using HAProxy’s capture feature to pass data from TCP mode to HTTP mode prompted me to update my SSL configuration. This was intended to get an A+ rating from SSL Labs by sending non-SNI capable clients to a server with weaker ciphers. This was to enable clients on WinXP/IE8, Java 6, and an old Android version to connect. I found a solution without having to have two sets of ciphers and handling traffic in both the TCP mode and HTTP mode. I then optimized my settings to a minimal list of cipher specifications.

My SSL hadn’t been significantly updated since I implemented fixes for the Poodle scare. Since then the general SSL landscape has changed significantly. SNI capability is widespread; SHA-1 keys signatures are deprecated; SSL certificate key sizes have grown; Lets Encrypt has launched; and more.

It was relatively easy to identify the issues:

  • A cipher was causing Java 6 to fail on DH key length.
  • 3DES ciphers are the only secure ciphers supported by WinXP/IE8.
  • The old Android supports a limited number of secure ciphers.
  • Some clients prefer ciphers that don’t provide forward security.

I also identified some key requirements for scoring well on the SSL Labs tests:

  • Enabling RC4 ciphers (OpenSSL Medium) reduces the score to B.
  • Only high strength ciphers is required to score A.
  • SNI capable clients connecting without Forward Security reduces the score to A-.
  • Strict Transport Security with a max-age of at least 180 days is required to score A+.

Apache2

My apache2 installation uses OpenSSL. Its HIGH grouping provides a sufficient set of secure ciphers. Ordering is largest key sizes first, you may want to break out the list and order SHA256 cipher ahead of SHA384. These setting rate an A+ with only the IE6 client unable to connect.

# Remove SSLv3 (-SSLv2 no longer available, but be safe)
SSLProtocol             all -SSLv2 -SSLv3

# Reorder one cipher to end of list for Java 6 - DH size > 1024
SSLCipherSuite          HIGH:+DHE-RSA-AES128-SHA

# Deal with clients that prefer 3DES (i.e. Microsoft Phones)
SSLHonorCipherOrder     on

# Known issues with compression - CRIME
SSLCompression          off

# Add Strict Transport Security header
Header always set Strict-Transport-Security "max-age=15552000; includeSubdomains;"

A more secure SSLCipherSuite would remove 3DES keys, and drops the reordering of ciphers. However, this will prevent most non-SNI clients from connecting on first try and possibly at all.

Apache adds !EXP:!aNULL:!eNULL to the specified SSLCipherSuite.

Exim4

My exim build uses GNUTLS instead of OpenSSL. This requires a different format. 3DES was disabled as only clients already broken are likely to use it. I already have a list of sites that don’t do TLS well, and are not offered the STARTTLS option.

SMTP over TLS is offered via STARTTLS. As noted above, many clients fail to with TLS and do not retry without TLS. Also most clients do not present valid client certificates. TLS is used transport security only, and other techniques are used to validate the remote server.

These configuration options are for a Ubuntu/Debian install. The capitalized values are used to set similarly named Exim configuration values.

# Enable SSMPT (port 465) to ease TLS cipher scanning - blocked at firewall.
daemon_smtp_ports = 25 : 465 : 587
tls_on_connect_ports = 465

# Use the normal ciphers; disable SSLv3 and 3DES ciphers
tls_require_ciphers = NORMAL:-VERS-SSL3.0:-3DES-CBC

# Don’t advertise STARTTLS to identified broken clients (never TLS?)
hostlist tls_hosts = ${if exists{/etc/exim4/tls_local_exceptions}\
                               {!/etc/exim4/tls_local_exceptions}\
MAIN_TLS_ADVERTISE_HOSTS = ${if !eq {$interface_port}{587}{+tls_hosts}{*}}

#  Try to verify client certificates - logging only, few clients verify 
MAIN_TLS_TRY_VERIFY_HOSTS  = ${if !eq {$interface_port}{587}{+tls_hosts}{}}

Dovecot

I initially had issues with configuring TLS. Dovecot uses OpenSSL but exposes the setting differently. It does not automatically disable NULL ciphers so those need to be added.

There is a limited set of internal clients. They use modern software so 3DES can safely be disabled.

# Increase the DH parameter size.
ssl_dh_parameters_length = 2048

# SSL protocols to use - specify disabled protocols - allow newer protocols
#ssl_protocols = TLSv1,TLSv1.1,TLSv1.2
ssl_protocols = !SSLv3,!SSLv2

# SSL ciphers to use - remove 3DES, ciphers always disabled by apache
ssl_cipher_list = HIGH:!3DES:!EXP:!aNULL:!eNULL

# Prefer the server's order of ciphers over client's.
ssl_prefer_server_ciphers = yes

Verification

After making the changes I verified:

  • All ciphers are secure (NMAP rating A).
  • Only TLS protocols are functional.
  • Additional checks on web site using SSL Labs.

Verification Tools

SSL Labs Server Test was used to verify the web server only. There is no support for SMTP or IMAP. It has a more fine grained rating system than nmap. This is appropriate as HTTPS appears to provide a larger attack surface.

Nmap was used to available cipher, protocols, and grade security. In my tests I only identified ratings of A, C (3DES), D (weak 3DES) and F (null ciphers enabled).

nmap –script ssl-enum-ciphers -p 993 localhost

OpenSSL was used to verify supported protocols; ssl3, tls1, tls1_1 and tls1_2. Mainly double check ssl3 is disabled. OpenSSL dropped inclusion of ssl2 so it can’t use it verify that ssl2 is disabled. It appears GnuTLS never included ssl2. So, it doesn’t seem to matter if ssl2 is enabled, as neither library support it.

echo | openssl s_client -ssl3 -connect localhost:993

Notes

TLSv1.1 seems redundant. Clients connect with either old (TLSv1) or new (TLSv1.2) protocols.

Many thanks to the developers of OpenSSL and GnuTLS for providing secure cipher sets. Both SSL distributions make cipher selection simpler by providing safe sets of ciphers. OpenSSLs set seems a little weak by including NULL ciphers with secure key exchange, but they are easily removed with by adding !aNULL to the cipher specification. I didn’t find any ciphers in the eNULL or EXP that needed to be removed from my selected ciphers.