不安全的域名验证¶
ID: java/unsafe-hostname-verification
Kind: path-problem
Security severity: 5.9
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-297
Query suites:
- java-code-scanning.qls
- java-security-extended.qls
- java-security-and-quality.qls
如果 HostnameVerifier
始终返回 true
,则它根本不会验证域名。这会阻止传输层安全性 (TLS) 提供任何安全性,并允许攻击者对应用程序执行中间人攻击。
攻击可能如下所示
程序连接到
https://example.com
。攻击者拦截此连接,并呈现一个看似有效的证书,该证书是他们自己选择的。
程序的
TrustManager
验证证书是否由可信证书颁发机构颁发。Java HTTPS 库检查证书是否已为主机
example.com
颁发。此检查失败,因为证书已为攻击者控制的域颁发,例如:malicious.domain
。HTTPS 库希望拒绝证书,因为域名与证书不匹配。在执行此操作之前,它会检查是否存在
HostnameVerifier
。您的
HostnameVerifier
被调用,它对任何证书都返回true
,因此也对这个证书返回true
。由于您的
HostnameVerifier
接受了证书,因此程序继续进行连接。攻击者现在可以读取您的程序发送到
https://example.com
的数据,或者在程序认为连接安全的情况下修改其回复。
建议¶
不要使用开放的 HostnameVerifier
。如果您在 TLS/HTTPS 配置中遇到了问题,您应该始终解决配置问题,而不是使用开放的验证器。
示例¶
在第一个(错误)示例中,HostnameVerifier
始终返回 true
。这允许攻击者执行中间人攻击,因为任何证书都会被接受,即使主机名不正确。在第二个(正确)示例中,HostnameVerifier
仅在证书正确检查后才返回 true
。
public static void main(String[] args) {
{
HostnameVerifier verifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true; // BAD: accept even if the hostname doesn't match
}
};
HttpsURLConnection.setDefaultHostnameVerifier(verifier);
}
{
HostnameVerifier verifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
try { // GOOD: verify the certificate
Certificate[] certs = session.getPeerCertificates();
X509Certificate x509 = (X509Certificate) certs[0];
check(new String[]{host}, x509);
return true;
} catch (SSLException e) {
return false;
}
}
};
HttpsURLConnection.setDefaultHostnameVerifier(verifier);
}
}
参考资料¶
Android 开发者:使用 HTTPS 和 SSL 保障安全.
Terse systems 博客:修复主机名验证.
常见漏洞枚举:CWE-297.