VNX-358: Hand-rolled TLS host name or certificate identity check uses a partial string match
Reports hand-written TLS identity checks (Java HostnameVerifier implementations and lambdas, certificate subject/issuer name tests, Python verify_hostname-style functions) that compare with endsWith/startsWith/contains/in instead of the standard matching rules.
| Field | Value |
|---|---|
| Rule ID | VNX-358 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Java, Python |
| CWE | CWE-358 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | improper-standard-check tls hostname-verification cwe-358 |
| Source | internal/sast/rules/vnx-358.rego |
Overview
VNX-358 maps to CWE-358: Improperly Implemented Security Check for Standard. TLS server identity has a standard (RFC 6125) and every platform ships an implementation of it. This rule reports hand-written replacements that check the identity with a partial string match:
- Java: a
HostnameVerifierverify(String host, SSLSession s)method, or a(host, session) ->lambda passed tosetHostnameVerifier/setDefaultHostnameVerifier/hostnameVerifier, that tests the host withendsWith,startsWith,contains,indexOformatchesand does not delegate to another verifier’sverify(). - Java: a certificate subject or issuer name (
getSubjectDN(),getSubjectX500Principal(),getIssuerDN(),getIssuerX500Principal()thengetName()), inline or through a variable assigned within six lines, tested withcontains,startsWith,endsWith,indexOformatches. - Python: a function named like
verify_hostname,match_hostorcheck_server_hostnamewhose host-name parameter is tested withendswith,startswithorfind, or is the right side ofx in host, unless the function also callsssl.match_hostnameor another verify routine.
Not reported: exact comparisons (equals, ==, a host looked up in an allowlist), a suffix test combined with a delegated verify() call, string tests on ordinary paths and names, Python host checks in functions that never touch a certificate, socket or SSL object (HTTP Host header validation is not TLS identity), test files, comments and string literals. A verifier that simply returns true is VNX-JAVA-006; switching verification off is VNX-CRYPTO-005.
Why This Matters
evilacme.com ends with acme.com, api.attacker.net starts with api., and any public CA will issue a certificate whose subject contains the text CN=api.acme.com inside another attribute. A partial match accepts all of them, so an attacker who can intercept traffic presents a certificate for a host they control and the client trusts it.
What Gets Flagged
// FLAGGED
public boolean verify(String hostname, SSLSession session) {
return hostname.endsWith("acme.com");
}
conn.setHostnameVerifier((h, s) -> h.endsWith(".acme.com"));
String subject = chain[0].getSubjectX500Principal().getName();
if (!subject.contains("CN=api.acme.com")) { throw new CertificateException("untrusted"); }
# FLAGGED
def match_host(cert, host):
cn = dict(x[0] for x in cert["subject"])["commonName"]
return cn in host
// NOT FLAGGED: restricts the host, then delegates to the standard verifier
return hostname.endsWith(".acme.com") && delegate.verify(hostname, session);
Remediation
- Use the platform verifier: the default
HostnameVerifier(or OkHttp’s), and in Python anssl.create_default_context()withcheck_hostname = True. - To trust a specific server, pin its public key (OkHttp
CertificatePinner, a pinnedTrustManager) rather than matching names as strings. - If names must be compared, compare the exact parsed name, never a substring.
References
- CWE-358: Improperly Implemented Security Check for Standard
- RFC 6125: Representation and Verification of Domain-Based Application Service Identity
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-358
// nosec vnx-358
Or record an ignore rule: vulnetix ignore add --rule vnx-358 --reason "...". See Suppressing findings.