CodeQL 文档

不安全的域名验证

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

单击以查看 CodeQL 代码库中的查询

如果 HostnameVerifier 始终返回 true,则它根本不会验证域名。这会阻止传输层安全性 (TLS) 提供任何安全性,并允许攻击者对应用程序执行中间人攻击。

攻击可能如下所示

  1. 程序连接到 https://example.com

  2. 攻击者拦截此连接,并呈现一个看似有效的证书,该证书是他们自己选择的。

  3. 程序的 TrustManager 验证证书是否由可信证书颁发机构颁发。

  4. Java HTTPS 库检查证书是否已为主机 example.com 颁发。此检查失败,因为证书已为攻击者控制的域颁发,例如:malicious.domain

  5. HTTPS 库希望拒绝证书,因为域名与证书不匹配。在执行此操作之前,它会检查是否存在 HostnameVerifier

  6. 您的 HostnameVerifier 被调用,它对任何证书都返回 true,因此也对这个证书返回 true

  7. 由于您的 HostnameVerifier 接受了证书,因此程序继续进行连接。

  8. 攻击者现在可以读取您的程序发送到 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);
	}

}

参考资料

  • ©GitHub, Inc.
  • 条款
  • 隐私