Improper Certificate Validation
Description
Improper Certificate Validation occurs when software does not validate, or incorrectly validates, a digital certificate. Certificates are used to establish trusted communication channels, verify identities, and ensure data integrity. When validation is missing or flawed, attackers can perform man-in-the-middle (MitM) attacks by presenting fraudulent certificates, intercepting encrypted communications, impersonating trusted servers, or injecting malicious content. Common issues include disabled hostname verification, accepting self-signed certificates, ignoring certificate expiration, and trusting invalid certificate chains.
Risk
Certificate validation failures directly enable man-in-the-middle attacks against otherwise encrypted communications. Attackers can intercept HTTPS traffic, steal credentials, inject malicious payloads, and impersonate legitimate services. The Netty framework's disabled hostname verification by default has affected numerous Java applications. Mobile apps frequently ship with certificate validation disabled for development and never re-enabled. Industrial control systems like Siemens Solid Edge have been vulnerable, enabling attacks on engineering workstations. Financial and healthcare data protected only by "TLS" becomes completely exposed when certificate validation fails.
Solution
Always validate certificates completely: verify the certificate chain up to a trusted root CA, check certificate expiration, validate that the hostname matches the certificate's Subject Alternative Names or Common Name, and verify certificate revocation status (CRL/OCSP). Use established TLS libraries with secure defaults. Never disable certificate validation in production. Implement certificate pinning for high-security applications. Test certificate validation explicitly during security assessments. Configure TLS properly with strong cipher suites.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Data Interception Attackers intercept and decrypt all "encrypted" communications when they can present forged certificates. |
| Integrity | Scope: Data Manipulation MitM attackers can modify data in transit, injecting malicious content or altering transactions. |
| Authentication | Scope: Identity Spoofing Attackers impersonate legitimate servers, directing users to malicious services. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Disabled hostname verification
import javax.net.ssl.*;
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String auth) {}
public void checkServerTrusted(X509Certificate[] chain, String auth) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
}, null);
// Accepts ANY certificate!
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
# VULNERABLE: Disabled certificate verification
import requests
# verify=False disables ALL certificate validation
response = requests.get('https://api.example.com', verify=False)
# VULNERABLE: Custom SSL context with no verification
import ssl
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
// VULNERABLE: Node.js with disabled verification
const https = require('https');
const agent = new https.Agent({
rejectUnauthorized: false // Accepts invalid certificates!
});
https.get('https://api.example.com', { agent }, (res) => {
// ...
});
// VULNERABLE: Environment variable
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // Never do this!
Fixed Code
// SAFE: Proper certificate validation
import javax.net.ssl.*;
import java.security.KeyStore;
public class SecureHttpClient {
public HttpsURLConnection createConnection(URL url) throws Exception {
// Load trusted certificates
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(getClass().getResourceAsStream("/truststore.jks"),
"password".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ctx.getSocketFactory());
// Default hostname verifier validates hostname
return conn;
}
}
// SAFE: Certificate pinning
public class PinningTrustManager implements X509TrustManager {
private static final String EXPECTED_PIN = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// Verify certificate chain first
defaultTrustManager.checkServerTrusted(chain, authType);
// Then verify pin
String pin = getPin(chain[0]);
if (!EXPECTED_PIN.equals(pin)) {
throw new CertificateException("Certificate pin mismatch");
}
}
}
# SAFE: Proper certificate verification (default)
import requests
# verify=True is the default - validates certificates
response = requests.get('https://api.example.com') # Secure by default
# SAFE: Custom CA bundle
response = requests.get('https://api.example.com',
verify='/path/to/ca-bundle.crt')
# SAFE: Certificate pinning with requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
class PinningAdapter(HTTPAdapter):
def __init__(self, expected_fingerprint):
self.expected_fingerprint = expected_fingerprint
super().__init__()
def init_poolmanager(self, *args, **kwargs):
ctx = create_urllib3_context()
kwargs['ssl_context'] = ctx
return super().init_poolmanager(*args, **kwargs)
def cert_verify(self, conn, url, verify, cert):
super().cert_verify(conn, url, verify, cert)
# Additional fingerprint verification
actual = conn.sock.getpeercert(binary_form=True)
if hashlib.sha256(actual).hexdigest() != self.expected_fingerprint:
raise SSLError("Certificate fingerprint mismatch")
// SAFE: Node.js with proper validation (default)
const https = require('https');
// Default behavior validates certificates
https.get('https://api.example.com', (res) => {
// Certificate validated automatically
});
// SAFE: Certificate pinning
const tls = require('tls');
const https = require('https');
const EXPECTED_FINGERPRINT = '00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF';
const agent = new https.Agent({
checkServerIdentity: (host, cert) => {
const fingerprint = cert.fingerprint256;
if (fingerprint !== EXPECTED_FINGERPRINT) {
throw new Error('Certificate fingerprint mismatch');
}
// Also perform standard hostname verification
return tls.checkServerIdentity(host, cert);
}
});
Exploited in the Wild
Siemens Solid Edge (Siemens, 2025)
CVE-2025-40744 in Siemens Solid Edge SE2025 allows MitM attacks due to improper client certificate validation when connecting to the License Service, with CVSS 7.5 high severity.
Fortinet FortiWeb WAF (Fortinet, 2024)
CVE-2024-33509 in FortiWeb allows MitM attackers to intercept and tamper with WAF communications due to improper certificate validation when fetching external data.
Netty Framework (Netty, Multiple)
Certificate hostname validation disabled by default in Netty 4.1.x affected numerous Java applications including Keycloak, making them vulnerable to MitM attacks.
Tools to test/exploit
-
SSLyze — analyze TLS configuration and certificate validation.
-
testssl.sh — TLS/SSL testing tool.
-
mitmproxy — test certificate validation by attempting MitM.
CVE Examples
-
CVE-2025-40744 — Siemens Solid Edge certificate validation bypass.
-
CVE-2024-33509 — FortiWeb improper certificate validation.
-
CVE-2022-32748 — Schneider Electric CAE credential leak.
References
-
MITRE. "CWE-295: Improper Certificate Validation." https://cwe.mitre.org/data/definitions/295.html
-
OWASP. "Transport Layer Protection Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html