03 Dec 2018 | Peter Stöckli
Missing TLS hostname verification in multiple Java libraries
Summary
Alphabot Security has looked at a bunch of popular Java communication libraries to check whether they verify that the hostname of the server they connect to is valid for the presented certificate.
Following Java libraries with missing hostname verification were found:
- CVE-2018-11087: Spring RabbitMQ Client
- CVE-2018-11775: Apache ActiveMQ Client
- CVE-2018-17187: Apache Qpid Proton-J
- and some libraries or applications that use the Jetty WebSocket client before 9.4.12 (like the JettyWebSocketClient of the Spring Framework)
The Vulnerability
Improper Validation of Certificate with Host Mismatch (CWE-297) is described as follows:
The software communicates with a host that provides a certificate, but the software does not properly ensure that the certificate is actually associated with that host. Even if a certificate is well-formed, signed, and follows the chain of trust, it may simply be a valid certificate for a different site than the site that the software is interacting with.
If the certificate's host-specific data is not properly checked - [..] - it may be possible for a redirection or spoofing attack to allow a malicious host with a valid certificate to provide data, impersonating a trusted host. In order to ensure data integrity, the certificate must be valid and it must pertain to the site that is being accessed.
Unfortunately, this kind of vulnerability is very common in the Java world since certificate verification and hostname verification are treated as two different parts, when in practice some sort of hostname verification is necessary to prevent MITM-attacks on all sorts of different protocols conveyed via TLS. (E.g. see RFC 2818/HTTP Over TLS).
Google provides some good documentation regarding Java and hostname verification with a focus on Android apps, including following warning:
Caution: SSLSocket does not perform hostname verification. It is up to your app to do its own hostname verification, preferably by calling getDefaultHostnameVerifier() with the expected hostname. Further beware that HostnameVerifier.verify() doesn't throw an exception on error but instead returns a boolean result that you must explicitly check.
Since Java 7 there’s another way of setting up hostname verification for libraries that require HTTPS-like hostname verification by calling setEndpointIdentificationAlgorithm
with the string-value 'HTTPS'
on the SSLParameters
of the SSLSocket
:
Important: If you use SSLContexts in your code always write tests that ensure that hostname verification works as expected.
The suboptimal Java API is often mirrored by libraries that use it, so that the user of the library has to set up hostname verification by himself. In our opinion the sensible thing to do for a library is to be secure by default, whilst allowing the user to turn off security features he deems unnecessary in his specific case.
Spring RabbitMQ Java Client
CVE: CVE-2018-11087
The Spring RabbitMQ Java Client (also known as Spring-AMQP) uses the official RabbitMQ Java Client to connect to RabbitMQ.
The official rabbitmq-java-client had some suboptimal API which only allowed to enable hostname verification
by providing an own SSLContext. In defense of the library it has JavaDoc on methods like useSslProtocol()
that state:
not recommended to use in production as it provides no protection against man-in-the-middle attacks.
However, the Spring RabbitMQ Java Client did not provide an own SSLContext and was as such never protected against MITM-attacks.
The rabbitmq-java-client has since implemented the method enableHostnameVerification()
, which makes it easier to enable
hostname verification.
The mitigation as described in the advisory:
- Upgrade to the 1.7.10.RELEASE or 2.0.6.RELEASE and set the enableHostnameValidation property to true. Override the transitive amqp-client version to at least 4.8.0 and 5.4.0, respectively
- The upcoming 2.1.0.RELEASE will set the property to true by default.
- If you are using the amqp-client library directly to create a connection factory, refer to its javadocs for the enableHostnameValidation() method.
Apache ActiveMQ Client (CVE-2018-11775)
CVE: CVE-2018-11775
The Apache ActiveMQ Client simply did not have hostname verification. This was fixed in Apache ActiveMQ 5.15.6, enabling hostname verification by default. So if you’re connecting to Amazon MQ or a similar service using the ActiveMQ Client you should upgrade to version 5.15.6 or later.
Jetty WebSocket Client
The Jetty WebSocket client before 9.4.12 had an SslContextFactory
configured that was potentially initialized without hostname verification.
The JettyWebSocketClient class of the Spring Framework which uses the Jetty WebSocket client underneath did not configure another SslContextFactory on its own.
Since version 9.4.12 Jetty does provide an SslContextFactory
with TLS hostname verification enabled.
Users of the Spring Frameworks that use the JettyWebSocketClient should upgrade to a framework version which includes a Jetty version of 9.4.12 or later.
So if you are using an older version of the Jetty WebSocket client you have to explicitly configure the SslContextFactory to get TLS hostname verification or simply upgrade your Jetty version to 9.4.12 and later.
Final Thoughts
- While TLS hostname verification is surely not on top of everyone's agenda it's still important that communication libraries perform it by default, because else it can simply undermine the benefits of using TLS and give a false sense of security.
- There are probably even more libraries in the Java world and in other ecosystems that don't perform TLS hostname verification by default.
- A shout-out to both the Apache Foundation and Pivotal (the company behind Spring). Both seem to have nice vulnerability management processes at work.