tomcat

Secure and harden Apache Tomcat's SSL/TLS

Reading time of 1183 words
6 minutes
Reading time of 1183 words ~ 6 minutes


Did you find this article helpful?
Please consider tipping me a coffee as a thank you.
Ko-fi Buy Me a Coffee
Did you find this article helpful? Please consider tipping me a coffee or three as a thank you.
Tip using Ko-fi or Buy Me a Coffee

In this guide I will walk through the process of hardening HTTPS connectors used by Apache Tomcat. As unfortunately the default configuration of Ubuntu 14.04 LTS using Tomcat 7 and OpenJDK 7 are vulnerable to a number of attacks and weak encryptions.

You can test your own site’s HTTPS implementation against these weaknesses at Qualys SSL Lab SSL Server Test. With this guide we can hopefully boost a F or even a B grade up to an A grade rating.

Alarmingly most default Tomcat over Java 7 HTTPS configurations usually receive an F grade due to some well known vulnerabilities that they permit. Such as the well publicised POODLE attack and the unauthenticated Diffie-Hellman man-in-the-middle key exchange attack.

Upgrading to a recent release of OpenJDK 8 will remove these vulnerabilities. But if you are not able to update Java you can still use this guide as it will improve your site’s HTTPS security.

Users of Apache HTTP Reverse-Proxy configurations should skip to the end of this article titled Apache HTTP Reverse-Proxy users. Where you’ll learn how to add a SSLCipherSuite directive into your VirtualHost configuration to harden HTTPS.

This guide is targeted towards Ubuntu 14.04 LTS but should work for other distributions.

Install OpenJDK 8 (Java 8)

I will be using a 3rd party ppa to install OpenJDK. On some setups using 3rd party PPAs could be considered a security risk so use your own discretion. Though this process will not delete existing Java installs so you can always revert back to your original install and configuration. Are PPA’s safe to add to my system…?

Check your Java version.

$ java -version

java version "1.7.0_65"

Install OpenJDK 8 headless.

sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install openjdk-8-jre-headless
ppa
jre

Switch your server to use your new OpenJDK 8 install.

sudo update-alternatives --config java

Select the listing that points to Java 8 OpenJDK, in my screenshot it is option 2.

/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1069 manual mode
update command

Recheck your Java version.

$ java -version

openjdk version "1.8.0_45-internal"

Now the final step is to configure your Apache Tomcat install to use OpenJDK 8.

sudo nano -B /etc/default/tomcat7

Search for the following comment block. In my config it was found at line #12.

#JAVA_HOME=/usr/lib/jvm/openjdk-6-jdk

Add the following variable below the comment block.

JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
edit JAVA_HOME

Restart Tomcat to use the new configuration.

sudo service tomcat7 restart

Note if you’re using Oracle’s implementation of Java JDK then you may need to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction. This removes Oracle’s imposed restriction that limits keys to a length of 128-bits. OpenJDK does not suffer this restriction. How to install Java Cryptography Extension (JCE) unlimited strength jurisdiction policy files.

Harden your Tomcat HTTPS implementation

A base configuration of a standard HTTPS connector, yours may look different.

<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               keystoreFile="/etc/ssl/rsa/example.com.pfx"
               keystoreType="PKCS12"
               keystorePass="changeit"
               connectionTimeout="20000"
               URIEncoding="UTF-8"
               compression="on"
               compressionMinSize="2048"
               compressableMimeType="text/html,text/xml,text/csv,text/css,text/javascript" />

You can discover what these various attributes do in the Tomcat 8 Configuration reference – The HTTP Connector.

The first major attribute change we need to implement is the protocol. In Tomcat 7 and below this is set to use the Java BIO Connector with the value of "HTTP/1.1" or "org.apache.coyote.http11.Http11Protocol".

Tomcat 7 users should change this attribute to use the Java NIO Connector which offers a similar functionality to the BIO (HTTP/1.1) connector with a smaller performance footprint. Tomcat 6 users should keep to the default BIO connector attribute while Tomcat 8 and later already use the NIO connector as default.

protocol="org.apache.coyote.http11.Http11NioProtocol"

The default Java 7 BIO and NIO connectors enable SSLv2 and SSLv3 protocols which are vulnerable to the POODLE attack. Connectors using OpenJDK 8+ have these insecure protocols disabled. All users of Java 7 SHOULD add the following attribute to disable this vulnerability.

sslEnabledProtocols="TLSv1, TLSv1.1, TLSv1.2"

The next attribute we’ll use to harden our setup is to manually select the encryption ciphers the connector is permitted to use when communicating with the a browser in HTTPS. Not all ciphers are equal and so I’ve compiled a list of 40 of the most secure yet compatible ciphers available in Java. This collection uses Forward Secrecy (FS) and disables weak algorithms such as the unauthorised Diffie–Hellman (DH) key exchange.

Unfortunately they break the ability to make successful HTTPS connections for many versions of Internet Explorer on Windows XP as well as on Android 2.3.7 or earlier. But for Java 7 users implementing this list is a must as the default collection used by its BIO and NIO connectors contain some weak and insecure ciphers.

Hardened Java Ciphers

ciphers="TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_RSA_WITH_RC4_128_SHA,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_EMPTY_RENEGOTIATION_INFO_SCSVF"

Finally Java 8+ users should add the useServerCipherSuitesOrder attribute and set it to true.

useServerCipherSuitesOrder="true"

By default Tomcat will use the first acceptable cipher presented by the client browser. But often this selection is not the strongest cipher available or supported. The previous cipher attribute lists the ciphers in a preferential order of strength. So by enabling useServerCipherSuitesOrder Tomcat will probe the client using this ordered sequence until a supported cipher is matched making sure the most secure connection available is always used.

After implementing the changes my harden connector looks like this. Again yours may look different depending on which Java edition is in use.

Hardened OpenJDK/Java HTTPS connector

 1<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"
 2    maxThreads="150" scheme="https" secure="true"
 3    keystoreFile="/etc/ssl/rsa/example.com.pfx"
 4    keystoreType="PKCS12"
 5    keystorePass="changeit"
 6    connectionTimeout="20000"
 7    URIEncoding="UTF-8"
 8    compression="on" compressionMinSize="2048"
 9    compressableMimeType="text/html,text/xml,text/csv,text/css,text/javascript"
10    useServerCipherSuitesOrder="true"
11    ciphers="TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
12    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
13    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
14    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
15    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
16    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
17    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
18    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
19    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
20    TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
21    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
22    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
23    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
24    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
25    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
26    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
27    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
28    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
29    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
30    TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
31    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
32    TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
33    TLS_ECDH_RSA_WITH_RC4_128_SHA,
34    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
35    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
36    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
37    TLS_RSA_WITH_AES_256_GCM_SHA384,
38    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
39    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
40    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
41    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
42    TLS_RSA_WITH_AES_128_GCM_SHA256,
43    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
44    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
45    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
46    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
47    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
48    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
49    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
50    TLS_EMPTY_RENEGOTIATION_INFO_SCSVF" />

Finally to implement your new configuration reboot Tomcat.

sudo service tomcat7 reboot

Congratulations you’re done. Go and retest your configuration with the Qualys SSL Lab SSL Server Test and hopefully your hardened configuration will receive an A grade.

ssl lab result

XML dumps of the modified connectors examples can be found on GitHub.

Apache HTTP Reverse-Proxy users

Users of Apache HTTPD in a Reverse-Proxy configuration do not need to reconfigure Tomcat. As Apache HTTP 2.4+ lets you add a simple environment variable directive into a site’s configuration.

sudo nano -B /etc/apache2/sites-available/[your site].conf

Add the following into the configuration block.

SSLCipherSuite HIGH:!aNULL:!MD5

Apache VirtualHost example

Listen 443

NameVirtualHost *:443
<VirtualHost *:443>
    SSLEngine On
    SSLCipherSuite HIGH:!aNULL:!MD5
    SSLCertificateFile /etc/apache2/ssl/file.pem
    ProxyPass / http://0.0.0.0:8080/
    ProxyPassReverse / http://0.0.0.0:8080/
</VirtualHost>

Learn more in the Apache HTTP documentation or how to setup a reverse-proxy.

References and sources used for this guide:

  1. How to Install OpenJDK 8 in Ubuntu 14.04 & 12.04 LTS
  2. Java Cryptography Architecture Oracle Providers Documentation for JDK 8
  3. Apache Tomcat 8 SSL/TLS Configuration HOW-TO
  4. Apache Tomcat 8 Configuration Reference The HTTP Connector

Written by Ben Garrett

Did you find this article helpful?
Please consider tipping me a coffee as a thank you.
Ko-fi Buy Me a Coffee
Did you find this article helpful? Please consider tipping me a coffee or three as a thank you.
Tip using Ko-fi or Buy Me a Coffee