【问题标题】:Certificate pinning ignoring hostname validation证书固定忽略主机名验证
【发布时间】:2015-07-03 16:38:24
【问题描述】:

我刚刚按照我找到的教程实施了证书固定。但现在我注意到它忽略了主机名验证,天知道它还忽略了什么。

这是我所拥有的:

SSLSocketFactory:

public class PinningSSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    PubKeyManager pkm;

    public PinningSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
        KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        pkm  = new PubKeyManager();

        TrustManager tm[] = { pkm };

        sslContext.init(null, tm, null);

    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
        UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }

    public void setContext(Context context) {
        pkm.setContext(context);
    }
} 

X509TrustManager:

public class PubKeyManager implements X509TrustManager {

    public Context mContext;

    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}

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

        if (chain == null) {
            throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
        }

        if (!(chain.length > 0)) {
            throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
        }

        if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {
            throw new CertificateException("checkServerTrusted: AuthType is not RSA");
        }

        // Perform customary SSL/TLS checks
        // get request cert
        try {
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
            tmf.init((KeyStore) null);

            for (TrustManager trustManager : tmf.getTrustManagers()) {
                ((X509TrustManager) trustManager).checkServerTrusted(chain, authType);
            }
        } catch (Exception e) {
            throw new CertificateException(e);
        }

        //get stored certificate

        expected = //compare both certs

        if (!expected) {
            throw new CertificateException("checkServerTrusted");
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

    public void setContext(Context context) {
        mContext = context;
    }
}

获取HttpClient:

public DefaultHttpClient getPinningHttpClient(){

    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        PinningSSLSocketFactory sf = new PinningSSLSocketFactory(trustStore);
        sf.setContext(context);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);

    } catch (Exception e) {
        e.printStackTrace();
    }

    return new DefaultHttpClient();
}

我的问题是如何解决缺少的主机名验证以及任何其他可能以这种方式被破坏的缺少验证。

感谢您提供的任何帮助。

【问题讨论】:

    标签: android validation ssl ssl-certificate


    【解决方案1】:

    证书/公钥固定的想法是您知道特定主机具有特定证书或公钥。仅当您不知道证书应该是什么时,才需要验证主机名和证书链。固定是您与预期同伴交谈的更有力的证据。但当然,您应该在应用程序中关联主机名和固定信息,即不要使用来自一个站点的固定信息来验证其他站点。

    除此之外,不要只使用“...我找到的教程”。在 OWASP,您可以找到有关该主题的 good information 以及示例代码。

    【讨论】:

    • 我基本上按照那个页面上的示例项目来做我现在所做的。问题是我们将证书放在质量服务器上,即使 url 与证书上的不匹配,它也接受了请求。所以我假设使用我的PinningSSLSocketFactory 以某种方式忽略了主机名验证。如果我使用默认的SSLSocketFactory,由于主机名验证,它不允许连接。
    • @HugoAlves:再一次 - 固定是更强大的验证方法,因为您知道证书或公钥。仅当您需要检查您获得的以前未知的证书是否可以信任时,才需要验证链和主机名。这意味着可以省略主机名检查。
    • 好的,感谢您的帮助。感觉这是一个我没有什么专业知识的领域我担心这是一种可能的攻击方式,将用户的请求重定向到攻击者的机器并窃取他们的信息,或者我已经打开了一些其他的攻击方式。我在想主机名验证会保护这个。
    • 我已经测试了将证书副本放在随机机器上并访问它。仅验证了公钥,机器的 url 与证书不同。如果我删除证书锁定代码,Android 会简单地拒绝连接并出现以下异常:java.io.IOException: Hostname 'myurl.com' was not verified。这不会为轻松重定向到攻击者的服务器打开一扇巨大的大门吗?
    • 如果我理解你的正确,你的问题是,在服务器受损和私钥丢失后,攻击者可以在其他服务器上使用相同的证书,而你不会注意到。这是真的,但问题不在于攻击者可以做到这一点,而是私钥被泄露,因此不再是私有的。因此,关于 TLS 安全性的基本假设被打破。在这种情况下,攻击者是从拥有的服务器、另一台服务器还是使用私钥对同一服务器名称进行中间人攻击,攻击者都没有关系。
    猜你喜欢
    • 2012-08-30
    • 1970-01-01
    • 2011-11-16
    • 2013-10-16
    • 1970-01-01
    • 2018-03-25
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多