【问题标题】:Disable SSLHandshakeException for a single connection为单个连接禁用 SSLHandshakeException
【发布时间】:2016-10-10 01:03:23
【问题描述】:

我正在寻找类似于this answer 的解决方案,但更安全。我想禁用证书验证,但仅针对单个请求(这就是我所需要的)。所以它应该做以下一项或多项

  • 一个请求完成后返回安全状态
  • 仅对给定 URL 禁用验证
  • (也许)只为一个线程使用不安全的设置

附录

当我要求更安全的解决方案时,我真的想知道这个问题(分数 -2)与原始问题(分数 +46)相比有什么问题。有人能解释一下吗?

解释我为什么需要这个:有一个有效的服务器证书,通常会被使用。但我需要向localhost 发送一个请求,它也必须在开发人员机器上工作。必须是 https,因为没有 http 支持。

【问题讨论】:

    标签: java security ssl https


    【解决方案1】:

    解决方案

    此实用程序类基于提供的示例。使用“宽松”的 SSLContext 预配置 TrustManager 接受所有证书。还需要一个主机名验证器,以防您的 localhost ssl 证书颁发给不同的 CN

    将其应用于需要使用HttpsURLConnection.setSocketFactoryHttpsURLConnection.setHostnameVerifier 进行“宽松”ssl 验证的每个连接。默认行为不会改变

    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    public class RelaxedSSLContext {
        // Create a trust manager that does not validate certificate chains like 
        public static TrustManager[] trustAllCerts = new TrustManager[] {
            new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() 
                    return null;
                }
                public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    //No need to implement. 
                }
                public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    //No need to implement. 
                }
            }
        };
    
        //hostname verifier. All hosts valid
        public static HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };  
    
        //hostname verifier. Only localhost and 127.0.0.1 valid
        public static HostnameVerifier localhostValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return "localhost".equals(hostname) || "127.0.0.1".equals(hostname);
            }
        };  
    
    
        public static SSLContext getInstance() throws KeyManagementException, NoSuchAlgorithmException{
            return getInstance("SSL");
        }   
    
        //get a 'Relaxed'  SSLContext with no trust store (all certificates are valids) 
        public static SSLContext getInstance(String protocol) throws KeyManagementException, NoSuchAlgorithmException{
            SSLContext sc = SSLContext.getInstance(protocol);
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            return sc;
        }   
    }
    

    这样使用

    HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
    conn.setSSLSocketFactory(RelaxedSSLContext.getInstance().getSocketFactory());
    conn.setHostnameVerifier(RelaxedSSLContext.localhostValid);
    

    示例 1(默认)

    url = "https://www.google.cop/finance";
    conn = url.openConnection();
    conn.connect();
    int statusCode = conn.getResponseCode();   // 200
    

    Example2(错误的主机名)

    url = "https://216.58.210.164/finance";
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    conn.setHostnameVerifier(RelaxedSSLContext.allHostsValid); 
    conn.connect();
    int statusCode = conn.getResponseCode(); //200
    // ERROR 'No subject alternative names matching IP address 216.58.210.164 found' without hostnameVerifier
    

    Example3(链不在默认信任库中)

    url = "https://www.aragon.es";
    HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
    conn.setSSLSocketFactory(RelaxedSSLContext.getInstance().getSocketFactory());
    conn.connect();
    int statusCode = conn.getResponseCode(); //200
    // ERROR 'unable to find valid certification path to requested target' without relaxedSocketFactory
    

    上下文

    我认为您的问题很切题、有用且表达得很好。在某些情况下是合适的,或者更确切地说,对于应用高安全性的上下文不是必需的。普通 HTTP 仍在使用中...

    让我们分析一下您的上下文。所需的是通过 HTTPS 访问本地主机而不信任服务器身份。这意味着您要接受服务器提供的任何证书。这种情况的安全问题是 MITM 附加(中间人)。来自(维基百科)

    攻击者秘密中继并可能改变它们之间的通信 两方认为他们正在与每一方直接沟通 其他。

    但是是否有可能通过不受信任的 https 连接到 localhost 进行 MITM 攻击

    https://security.stackexchange.com/questions/8145/does-https-prevent-man-in-the-middle-attacks-by-proxy-server/8309#8309

    首先,在 https 中,服务器必须向客户端提供服务器证书。客户端验证证书的公钥并检查是否与本地信任库匹配。 在网络管理员的合作下,可以嗅探网络并设置代理来拦截连接。但是恶意代理不拥有匹配的私钥,因此代理服务器可能会尝试伪造证书并提供自己的公钥。 证书不会出现在客户端信任库上,因此连接将被拒绝。但是如果去掉信任库验证,理论上攻击是可能的。

    但是,MITM 是否可以限制与 localhost 的连接?

    https://security.stackexchange.com/questions/61399/are-mitm-attacks-possible-on-http-loopbacks

    在一般情况下这是不可能的,但对机器具有 root 访问权限的攻击者可以更改 DNS 并将请求重定向到恶意代理。即使使用 127.0.0.1,如果应用程序有办法配置连接端口,也是可能的。

    偏执的解决方案可能是硬编码服务器连接 url、端口甚至信任。但是你说的是开发环境中的 localhost,所以我想我们可以放松一点

    【讨论】:

      【解决方案2】:

      只需使用实例方法setX() 而不是静态setDefaultX()

      HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
      connection.setSSLSocketFactory(...);
      connection.setHostnameVerifier(...);
      

      【讨论】:

        【解决方案3】:

        对此的解决方案始终是将服务器证书导入客户端信任库,或者,如果服务器证书在您的控制之下,则最好获取由受信任的 CA 签名的服务器证书。你不能在代码中安全地做到这一点,你不应该想“只是为了开发”。否则,您不会在开发中测试生产代码。

        【讨论】:

          猜你喜欢
          • 2012-04-16
          • 1970-01-01
          • 2018-03-30
          • 1970-01-01
          • 2023-03-03
          • 2017-10-24
          • 1970-01-01
          • 2017-12-08
          相关资源
          最近更新 更多