CWE-297: Improper Validation of Certificate with Host Mismatch
Overview
A TLS client that does not check that the certificate identifies the host it is connecting to will accept any valid certificate, including one an attacker holds for a different domain, which allows a man-in-the-middle attack. Modern server certificates identify the host with Subject Alternative Name (SAN) entries; Common Name-only certificates are legacy and should be replaced rather than treated as the compatibility target.
Relationship to Other CWEs
- CWE-297 (this page) - the name: the client does not check that the certificate identifies the host it connected to, so it accepts any valid certificate, including one an attacker holds for a domain of their own.
- CWE-295 (Improper Certificate Validation) - the parent MITRE records for this page and for the other certificate checks listed here. Use it when validation is switched off wholesale rather than failing one specific check; the language-specific pages live there.
- CWE-296 (Improper Following of a Certificate's Chain of Trust) - the chain: the certificate does not lead to a trusted root, whether it is self-signed, signed by an untrusted CA, or missing a valid intermediate.
- CWE-298 (Improper Validation of Certificate Expiration) - the dates: the certificate is accepted outside the validity window its issuer set.
- CWE-299 (Improper Check for Certificate Revocation) - revocation: the chain is valid and the certificate is in date, but the issuer has withdrawn it since.
OWASP Classification
A07:2025 - Authentication Failures
Risk
High: An attacker holding a valid certificate for any domain can impersonate the server, read the credentials and data the client sends, and take over its sessions. Because any valid certificate is accepted regardless of hostname, TLS authenticates nobody.
Remediation Steps
Core Principle: Never establish a TLS connection unless the TLS stack has checked the certificate's hostname against the peer the application asked for; do not bypass or weaken that check.
Locate the hostname validation vulnerability
- Start from the file and line in the finding, then look at the TLS client code around it: HTTPS requests, or anything else that establishes a secure connection
- Find where hostname verification is off: a
HostnameVerifierthat returns true,check_hostname=False, awrap_socket()call with noserver_hostname, a rawSSLSocketwith no endpoint identification algorithm, or verification disabled outright withverify=False/SSL_VERIFY_NONE - Check every place the process creates a TLS client or context, not only the one the finding names
Always verify hostname (Primary Defense)
- HTTP clients: verify the hostname by default -
requestsandurllib3in Python, OkHttp, Apache HttpClient andjava.net.http.HttpClientin Java. Below the HTTP client the default runs the other way; see the next bullet. - Switch the name check on for raw TLS sockets: a Java
SSLSocketand a PythonSSLContext.wrap_socket()called withoutserver_hostnameboth verify the chain and skip the name, which authenticates nobody. Nothing is disabled in either case and noverify=Falserule matches. In Java, setSSLParameters.setEndpointIdentificationAlgorithm("HTTPS")and apply the parameters to the socket beforestartHandshake(); in Python, passserver_hostname=hosttowrap_socket()rather than clearingcheck_hostname. See CWE-295 Java and CWE-295 Python. - Never disable hostname verification: remove trust-all
HostnameVerifierimplementations,check_hostname=False,assert_hostname=False, and custom validators that skip the name check - SAN, not Common Name: the library should compare the hostname with the certificate's Subject Alternative Names; replace legacy Common Name-only certificates instead of weakening validation to accept them
- Use the library's built-in hostname verification: custom validators often have security flaws of their own; write one only when there is no alternative
Use secure TLS configuration
- Chain-verification switches:
verify=False(Pythonrequests),ssl.CERT_NONEand OpenSSL'sSSL_VERIFY_NONEturn certificate verification off entirely, so they take the name check with them. Removing them is necessary, but on its own it does not give you a name check - see the next bullet. - Hostname-verification switches: in the JDK, a
HostnameVerifierwhoseverify()returnstrue; the JDK itself has noALLOW_ALLconstant. In Apache HttpClient,NoopHostnameVerifier.INSTANCE; the 4.x nameALLOW_ALL_HOSTNAME_VERIFIERwas deprecated in 4.4 but is still a public field throughout the 4.5.x line, so a finding on 4.5 will name it, and it is gone in 5.x. In Python,check_hostname = Falseorassert_hostname=False. In OpenSSL the name check is not a switch at all: it is opt-in throughSSL_set1_host()orX509_VERIFY_PARAM_set1_host(), andSSL_VERIFY_PEERon its own gives a fully validated chain with no name check whatsoever. - Trust store: rely on the one the runtime already uses rather than a hand-built one, and note that it is not always the OS store - see CWE-295
- Verify the whole chain: not just the leaf certificate
Handle certificate errors properly
- Do not catch and ignore SSL exceptions; let the connection fail, and treat the failure as a security issue rather than something to work around
- Log the hostname, certificate details, and error message when validation fails, so the failure can be investigated
- Alert on frequent certificate errors, which can indicate a MITM attempt
Pin public keys for critical connections (Defense in Depth)
- Pin expected public key/SPKI hashes for high-security connections
- Validate against pinned values in addition to normal hostname verification
- Update pins during key rotation and maintain backup pins for smooth rotation
- Use certificate transparency logs to detect mis-issued certificates
Test the hostname verification fix
- Confirm a valid public endpoint still connects (
https://badssl.com/returns 200). A client that rejects every certificate passes every test below, so assert the accept first - Verify connections fail when certificate hostname doesn't match (
https://wrong.host.badssl.com/), through each TLS client the process creates rather than only the one the finding named - Test with self-signed certificates (should fail unless explicitly trusted)
- Test with expired certificates (should fail)
- Test with valid certificate for different domain (should fail)
- Re-scan with security scanner to confirm the issue is resolved
Common Vulnerable Patterns
- A
HostnameVerifierthat returnstrue, orNoopHostnameVerifier.INSTANCE check_hostname = Falseorassert_hostname=FalseSSLContext.wrap_socket()called withoutserver_hostname- A raw
SSLSocketwith no endpoint identification algorithm set - OpenSSL verification with
SSL_VERIFY_PEERbut noSSL_set1_host() - Custom validators that walk the chain and never compare the name
verify=False,CERT_NONEorSSL_VERIFY_NONE, which disable chain verification and take the name check with them