CodeQL 文档

TrustManager 接受所有证书

ID: java/insecure-trustmanager
Kind: path-problem
Security severity: 7.5
Severity: error
Precision: high
Tags:
   - security
   - external/cwe/cwe-295
Query suites:
   - java-code-scanning.qls
   - java-security-extended.qls
   - java-security-and-quality.qls

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

如果 TrustManagercheckServerTrusted 方法从不抛出 CertificateException,则它会信任所有证书。这允许攻击者对应用程序执行中间人攻击,从而破坏传输层安全 (TLS) 提供的任何安全性。

攻击可能如下所示

  1. 易受攻击的程序连接到 https://example.com

  2. 攻击者拦截此连接,并为 https://example.com 提供有效的自签名证书。

  3. 易受攻击的程序调用 checkServerTrusted 方法以检查是否应该信任该证书。

  4. 您的 TrustManagercheckServerTrusted 方法不抛出 CertificateException

  5. 易受攻击的程序接受证书并继续连接,因为您的 TrustManager 通过不抛出异常而隐式地信任它。

  6. 攻击者现在可以读取您的程序发送到 https://example.com 的数据,或在程序认为连接安全的情况下修改其回复。

建议

不要使用信任任何证书的自定义 TrustManager。如果您必须使用自签名证书,不要信任所有证书,而只信任此特定证书。有关如何执行此操作的示例,请参见下文。

示例

在第一个(错误)示例中,TrustManager 永远不会抛出 CertificateException,因此隐式地信任任何证书。这允许攻击者执行中间人攻击。在第二个(正确)示例中,要信任的自签名证书被加载到 KeyStore 中。这明确地定义了证书为可信的,并且不需要创建自定义 TrustManager

public static void main(String[] args) throws Exception {
    {
        class InsecureTrustManager implements X509TrustManager {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // BAD: Does not verify the certificate chain, allowing any certificate.
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }
        }
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManager[] trustManager = new TrustManager[] { new InsecureTrustManager() };
        context.init(null, trustManager, null);
    }
    {
        SSLContext context = SSLContext.getInstance("TLS");
        File certificateFile = new File("path/to/self-signed-certificate");
        // Create a `KeyStore` with default type
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        // `keyStore` is initially empty
        keyStore.load(null, null);
        X509Certificate generatedCertificate;
        try (InputStream cert = new FileInputStream(certificateFile)) {
            generatedCertificate = (X509Certificate) CertificateFactory.getInstance("X509")
                    .generateCertificate(cert);
        }
        // Add the self-signed certificate to the key store
        keyStore.setCertificateEntry(certificateFile.getName(), generatedCertificate);
        // Get default `TrustManagerFactory`
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        // Use it with our key store that trusts our self-signed certificate
        tmf.init(keyStore);
        TrustManager[] trustManagers = tmf.getTrustManagers();
        context.init(null, trustManagers, null);
        // GOOD, we are not using a custom `TrustManager` but instead have
        // added the self-signed certificate we want to trust to the key
        // store. Note, the `trustManagers` will **only** trust this one
        // certificate.
        
        URL url = new URL("https://self-signed.badssl.com/");
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(context.getSocketFactory());
    }
}

参考资料

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