VNX-JAVA-006 – Insecure TLS Trust Manager
Overview
This rule detects two closely related patterns that render TLS completely ineffective: (1) an X509TrustManager whose checkServerTrusted() method has an empty body, meaning it performs no validation and silently accepts any certificate presented by any server; and (2) named patterns such as ALLOW_ALL_HOSTNAME_VERIFIER, TrustAllCerts, TrustAll, and NullTrustManager that signal a hostname verifier which always returns true. Both patterns are covered by CWE-295 (Improper Certificate Validation).
Severity: Critical | CWE: CWE-295 – Improper Certificate Validation
Why This Matters
TLS’s security guarantees rest entirely on two checks: (a) the server’s certificate is signed by a trusted certificate authority, and (b) the certificate’s Common Name or Subject Alternative Names match the hostname the client intended to connect to. Disabling either check means that any attacker who can intercept the network traffic — on a shared Wi-Fi network, inside a corporate proxy, via BGP hijacking, or through a compromised DNS resolver — can silently substitute their own certificate. The client connects to the attacker’s server, believing it is speaking to the intended destination. All traffic is decrypted, modified, and re-encrypted in a classic man-in-the-middle (MitM) position.
In a microservices environment, a MitM position between internal services means an attacker who has already breached one service can intercept API calls, steal JWT tokens, modify responses in flight, and exfiltrate sensitive data without triggering any TLS alarm. These patterns are commonly introduced during development (“I just need to bypass the self-signed certificate warning”) and then forgotten in production code.
What Gets Flagged
The rule matches two patterns. First, named trust-manager bypass classes:
// FLAGGED: ALLOW_ALL_HOSTNAME_VERIFIER disables hostname checking
HttpsURLConnection.setDefaultHostnameVerifier(
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
// FLAGGED: custom "trust all" verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true; // accepts any hostname
}
};
Second, an empty checkServerTrusted implementation:
// FLAGGED: empty body means no certificate validation occurs
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
// ^^ empty — flagged
}
};
Remediation
Remove the custom trust manager entirely and rely on the JVM’s built-in trust store. The JVM ships with a curated CA bundle (
$JAVA_HOME/lib/security/cacerts). If the server’s certificate is issued by a public CA, no custom trust manager is required.// SAFE: use the default SSL context — JVM validates certificates automatically HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection(); // No custom SSLContext, no custom HostnameVerifier neededFor self-signed or private CA certificates, load the CA into a custom trust store — do not disable validation. Import the CA certificate into a
KeyStore, then construct anSSLContextfrom it. This preserves full certificate chain and hostname validation while trusting your internal PKI.// SAFE: trust a specific private CA certificate, validate everything else normally KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); try (InputStream caStream = getClass().getResourceAsStream("/certs/internal-ca.jks")) { ks.load(caStream, "changeit".toCharArray()); } TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection(); conn.setSSLSocketFactory(sslContext.getSocketFactory()); // Default HostnameVerifier is used — hostname validation is intactImplement a real
X509TrustManagerif you need custom logic. Delegate to the JVM’s defaultTrustManagerfirst, then apply additional constraints. Never leavecheckServerTrustedempty.// SAFE: extends default trust, adds extra constraint public class PinningTrustManager implements X509TrustManager { private final X509TrustManager defaultTm; public PinningTrustManager() throws Exception { TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init((KeyStore) null); this.defaultTm = (X509TrustManager) tmf.getTrustManagers()[0]; } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { defaultTm.checkServerTrusted(chain, authType); // delegates — never empty // Optionally: pin to a specific leaf or intermediate cert } @Override public X509Certificate[] getAcceptedIssuers() { return defaultTm.getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { defaultTm.checkClientTrusted(chain, authType); } }Enforce a minimum TLS version. Configure the
SSLContextor server properties to require at least TLS 1.2 (TLSv1.2), preferably TLS 1.3.Use Spring’s
RestTemplateorWebClientwith a properSSLContext. If you use Spring’s HTTP client abstractions, configure anHttpComponentsClientHttpRequestFactorybacked by anSSLContextbuilt as above, rather than installing a JVM-wide bypass.