【问题标题】:HTTPS connections over proxy servers通过代理服务器的 HTTPS 连接
【发布时间】:2010-10-05 16:54:15
【问题描述】:

是否可以通过代理服务器进行 HTTPS 连接?如果是,什么样的代理服务器允许这样做?

How to use Socks 5 proxy with Apache HTTP Client 4?重复

【问题讨论】:

标签: proxy https


【解决方案1】:

TLS/SSL(HTTPS 中的 S)保证您和您正在联系的服务器之间没有窃听者,即没有代理。通常,您使用CONNECT 通过代理打开 TCP 连接。在这种情况下,代理将无法缓存、读取或修改任何请求/响应,因此毫无用处。

如果希望代理能够读取信息,可以采取以下方法:

  1. 客户端启动 HTTPS 会话
  2. 代理透明地拦截连接并 返回一个临时生成的(可能 弱)证书Ka, 由证书颁发机构签署 这是无条件信任的 客户。
  3. 代理启动到目标的 HTTPS 会话
  4. 代理验证 SSL 的完整性 证书;如果 证书无效。
  5. 代理流内容,解密它 并重新加密它 K一个
  6. 客户端显示内容

一个例子是 Squid 的SSL bump。同样,打嗝can be configured 来执行此操作。这也是used in a less-benign context by an Egyptian ISP

请注意,现代网站和浏览器可以使用 HPKPbuilt-in certificate pins 来破坏这种方法。

【讨论】:

  • 这在原则上是可行的,但这不是浏览器与 HTTP 代理进行 HTTPS 请求对话的方式。这里描述的方式意味着代理服务器实际上是一个中间人(因此必须相应地被信任)。
  • 鱿鱼会这样做。它被称为SSL Bump
  • 在没有向最终用户发出大量警报的情况下将无法工作。 “客户无条件信任” - 没有这样的事情。即使证书是完美的-AAA+++,它仍然显示不同的域与最终用户要求的不匹配,这将使任何理智的浏览器(这里不意味着 IE ......)上下跳跃尖叫。当然,可以将 wget 与禁用 SSL 检查的参数一起使用,但你猜怎么着?在禁用核心安全检查后,此连接不能再命名为“SSL”。
  • @Van Jone Unconditionally trusted 指的是 CA 证书。 CA 证书没有域。我已经用两个示例修改了答案,这些示例在实践中确实/确实有效,而不会向用户发出任何警报。
  • 我的回答依赖于你所谓的“假 CA”。 CA 的证书无条件受信任的,要么是因为用户(或其计算机上的软件,例如企业配置或恶意软件)以这种方式配置它,要么是因为 CA 是从其中一个 CA 获得的受到主要浏览器的信任,例如在 MCS 案例中。代理会为客户端请求的每个域生成一个新的有效证书,因此如果没有答案末尾提到的反 MITM 设施,客户端将不会注意到。
【解决方案2】:

简短的回答是:这是可能的,并且可以使用特殊的 HTTP 代理或 SOCKS 代理来完成。

首先,HTTPS 使用 SSL/TLS,它通过在不安全的通道上建立安全的通信通道来确保端到端的安全性。如果 HTTP 代理能够看到内容,那么它就是中间人窃听者,这违背了 SSL/TLS 的目标。因此,如果我们想通过普通的 HTTP 代理进行代理,那么肯定有一些技巧在发挥作用。

诀窍是,我们使用名为CONNECT 的特殊命令将 HTTP 代理转换为 TCP 代理。并非所有 HTTP 代理都支持此功能,但现在很多都支持。 TCP 代理无法看到以明文形式传输的 HTTP 内容,但这不会影响它来回转发数据包的能力。这样,客户端和服务器就可以在代理的帮助下相互通信。这是代理 HTTPS 数据的安全方式。

还有一种不安全的做法,即 HTTP 代理成为中间人。它接收客户端发起的连接,然后发起另一个到真实服务器的连接。在实现良好的 SSL/TLS 中,客户端将被通知代理不是真正的服务器。因此,客户端必须通过忽略警告来信任代理才能正常工作。之后,代理只需解密来自一个连接的数据,重新加密并将其提供给另一个连接。

最后,我们当然可以通过SOCKS 代理来代理HTTPS,因为SOCKS 代理工作在较低级别。您可能会认为 SOCKS 代理既是 TCP 代理又是 UDP 代理。

【讨论】:

  • 使用 CONNECT 是否会引起stackoverflow.com/a/3118759/632951 中提到的安全警告?
  • @Pacerier 我不这么认为。在 CONNECT 模式下,代理工作在传输层。
  • 那么,通过 CONNECT 方法,来自客户端的任何 https 数据不会传递到中间代理的应用程序级别吗?并且只是在代理的 TCP 级别进行评估并直接中继到远程服务器
  • @zzinny TCP CONNECT 请求仅用于通过代理打开到请求的 Web 服务器的 TCP 连接。 proxy只关心传输层(TCP连接),不接触更高层的任何PDU。
【解决方案3】:

据我所知,您需要在代理上使用 HTTP CONNECT 查询。这会将请求连接转换为透明的 TCP/IP 隧道。

所以你需要知道你使用的代理服务器是否支持这个协议。

【讨论】:

  • 确实,客户端使用 CONNECT 动词通过 HTTP 代理服务器使用 https:// URI。在这种情况下,连接通过代理建立隧道,因此证书验证照常完成,就好像客户端直接与终端服务器对话一样。
  • @chburd,但是代理通常支持 HTTP CONNECT 吗?
【解决方案4】:

如果它仍然感兴趣,这里是一个类似问题的答案: Convert HTTP Proxy to HTTPS Proxy in Twisted

回答问题的第二部分:

如果是,什么样的代理服务器 允许吗?

开箱即用,大多数代理服务器将配置为仅允许 HTTPS 连接到端口 443,因此具有自定义端口的 https URI 将不起作用。这通常是可配置的,具体取决于代理服务器。例如,Squid 和 TinyProxy 支持这一点。

【讨论】:

    【解决方案5】:

    这是我使用 SOCKS 代理同时支持 HTTP 和 HTTPS 请求的完整 Java 代码。

    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.net.Proxy;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    
    import org.apache.http.HttpHost;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.protocol.HttpClientContext;
    import org.apache.http.config.Registry;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.conn.socket.ConnectionSocketFactory;
    import org.apache.http.conn.socket.PlainConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    import org.apache.http.protocol.HttpContext;
    import org.apache.http.ssl.SSLContexts;
    import org.apache.http.util.EntityUtils;
    
    import javax.net.ssl.SSLContext;
    
    /**
     * How to send a HTTP or HTTPS request via SOCKS proxy.
     */
    public class ClientExecuteSOCKS {
    
        public static void main(String[] args) throws Exception {
            Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", new MyHTTPConnectionSocketFactory())
                .register("https", new MyHTTPSConnectionSocketFactory(SSLContexts.createSystemDefault
                    ()))
                .build();
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
            try (CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .build()) {
                InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234);
                HttpClientContext context = HttpClientContext.create();
                context.setAttribute("socks.address", socksaddr);
    
                HttpHost target = new HttpHost("www.example.com/", 80, "http");
                HttpGet request = new HttpGet("/");
    
                System.out.println("Executing request " + request + " to " + target + " via SOCKS " +
                    "proxy " + socksaddr);
                try (CloseableHttpResponse response = httpclient.execute(target, request, context)) {
                    System.out.println("----------------------------------------");
                    System.out.println(response.getStatusLine());
                    System.out.println(EntityUtils.toString(response.getEntity(), StandardCharsets
                        .UTF_8));
                }
            }
        }
    
        static class MyHTTPConnectionSocketFactory extends PlainConnectionSocketFactory {
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
                Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
                return new Socket(proxy);
            }
        }
    
        static class MyHTTPSConnectionSocketFactory extends SSLConnectionSocketFactory {
            public MyHTTPSConnectionSocketFactory(final SSLContext sslContext) {
                super(sslContext);
            }
    
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
                Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
                return new Socket(proxy);
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      您可以使用中间人技术和动态 SSL 生成来完成此操作。看看mitmproxy - 它是一个基于 Python 的、支持 SSL 的 MITM 代理。

      【讨论】:

        【解决方案7】:

        通过 SSH 建立隧道(端口转发)HTTPS(Linux 版本):

        1. localhost 上使用 443 关闭。

        2. 以 root 身份启动隧道:

          ssh -N login@proxy_server -L 443:target_ip:443
          
        3. 127.0.0.1 target_domain.com 添加到/etc/hosts

        您在本地主机上所做的一切。
        然后:target_domain.com 可以从localhost 浏览器访问。

        【讨论】:

          【解决方案8】:

          我不认为“通过代理服务器建立 HTTPS 连接”是指代理服务器的中间人攻击类型。我认为这是在询问是否可以通过 TLS 连接到 http 代理服务器。答案是肯定的。


          是否可以通过代理服务器进行 HTTPS 连接?

          是的,请参阅我的问题和答案 这里。 HTTPs proxy server only works in SwitchOmega

          如果是,什么样的代理服务器允许这样做?

          这种代理服务器部署 SSL 证书,就像普通网站一样。但是您需要一个pac 文件供浏览器通过 SSL 配置代理连接。

          【讨论】:

            【解决方案9】:

            我试过了

            • 开始隧道:ssh -N -D 12345 login@proxy_server
            • 在firefox设置中设置代理为localhost:12345
              • 并勾选“将此代理用于所有协议”

            但是每当我尝试连接到 https 网站时,都会导致错误“连接不安全”。

            解决办法是

            • “取消勾选”“将此代理用于所有协议”
            • 仅将代理“localhost:12345”设置为 SOCKS 代理
            • 并将 HTTP 代理、SSL 代理、FTP 代理留空

            来自数字海洋文献的参考

            How To Route Web Traffic Securely Without a VPN Using a SOCKS Tunnel

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-06-03
              • 1970-01-01
              • 2023-03-29
              • 2014-06-27
              • 1970-01-01
              相关资源
              最近更新 更多