代理服务器上的HTTPS连接

137

我认为这与https://dev59.com/q2Ag5IYBdhLWcg3w_vOE不重复。 - Uri
1
是的,这是可能的。在此处查看实际示例 https://dev59.com/clMI5IYBdhLWcg3wIX27 - Rick
9个回答

84
TLS/SSL(HTTPS中的S)保证你和服务器之间不存在窃听者,即没有代理。通常,你可以使用CONNECT来打开通过代理的TCP连接。在这种情况下,代理将无法缓存、读取或修改任何请求/响应,因此没有什么用处。
如果你希望代理能够读取信息,可以采取以下方法:
1、客户端启动HTTPS会话 2、代理透明地拦截连接并返回一个临时生成的(可能是弱的)证书Ka,由证书机构签名,该证书机构被客户端无条件地信任。 3、代理开始向目标发起HTTPS会话 4、代理验证SSL证书的完整性;如果证书无效,则显示错误。 5、代理流式传输内容、解密它并使用Ka重新加密它 6、客户端显示内容
Squid的SSL bump是一个例子。类似地,burp可以进行配置来执行此操作。埃及一家ISP曾经在一个不那么良性的情况下使用过
请注意,现代网站和浏览器可以使用HPKP内置证书强制来防御这种方法。

14
原则上这是可行的,但这并不是浏览器用于HTTPS请求与HTTP代理服务器通信的方式。这里所描述的方式暗示着代理服务器实际上是一个中间人攻击(因此必须得到相应的信任)。 - Bruno
6
鱿鱼做到了这一点。这被称为“SSL Bump”(SSL突破)。 - Adam Mackler
4
若没有大量向终端用户发出警报,将无法正常工作。"客户端无条件信任" - 没有这样的事情。即使证书是完美的AAA + ++,它仍然显示不匹配终端用户请求的不同域名,这会让任何正常的浏览器(这里不是指IE...)跳来跳去地尖叫。当然,可以使用wget并禁用SSL检查参数,但猜猜怎么着?在核心安全检查被禁用后,此连接将无法再被称为"SSL"。 - Van Jone
2
@Van Jone,“无条件信任”是指CA证书。CA证书没有域。我已经修改了答案,并提供了两个实际情况下有效而不向用户发出任何警报的示例。 - phihag
8
我的答案依赖于你所谓的“虚假CA”。该CA的证书被绝对信任,要么是因为用户(或他电脑上的软件,例如企业配置或恶意软件)进行了相应配置,要么是因为该CA是从主流浏览器信任的CA之一获取的,比如MCS案例中的情况。代理服务器会为客户端请求的每个域生成一个新的有效证书,所以如果没有答案末尾提到的反钓鱼和反中间人攻击功能,客户端将不会注意到这一点。 - phihag
显示剩余5条评论

37
简短的回答是:可以通过特殊的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会导致安全警告吗,就像https://dev59.com/8U7Sa4cB1Zd3GeqP5avU#3118759中提到的那样? - Pacerier
1
@Pacerier 我不这么认为。在CONNECT模式下,代理工作在传输层。 - Cyker
2
那么,通过CONNECT方法,客户端的任何https数据都不会传递到中间代理的应用程序级别?只是在代理的TCP级别上进行评估,并直接中继到远程服务器? - zzinny
@zzinny TCP连接请求仅用于通过代理打开到所请求的Web服务器的TCP连接。代理仅关注传输层(TCP连接),不涉及任何更高层次的PDU。 - dombg

16
据我所记,您需要在代理服务器上使用HTTP CONNECT查询。这将把请求连接转换为透明的TCP/IP隧道。
因此,您需要了解所使用的代理服务器是否支持此协议。

3
客户端使用CONNECT动词通过HTTP代理服务器使用https:// URI。在这种情况下,连接通过代理进行隧道传输,因此证书验证与客户端直接与终端服务器交互时一样进行。 - Bruno
1
@chburd,但是代理通常支持HTTP CONNECT吗? - Pacerier

10
如果您仍然有兴趣,这里有一个类似问题的答案: 将 Twisted 中的 HTTP 代理转换为 HTTPS 代理 回答问题的第二部分:
大多数代理服务器默认只允许通过端口443进行HTTPS连接,因此具有自定义端口的https URI将无法工作。这通常可以根据代理服务器进行配置。例如,Squid和TinyProxy支持此功能。

6
这是我完整的Java代码,支持使用SOCKS代理进行HTTP和HTTPS请求。
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);
        }
    }
}

3
通过SSH进行隧道(端口转发)HTTPS(Linux版本):
  1. Turn off using 443 on localhost.

  2. Start tunneling as root:

    ssh -N login@proxy_server -L 443:target_ip:443
    
  3. Adding 127.0.0.1 target_domain.com to /etc/hosts.

无论您在本地主机上做什么。
然后:target_domain.com 可以从 localhost 浏览器访问。


3
您可以使用动态SSL生成的中间人技术来实现这一点。看看mitmproxy - 它是一个基于Python的、支持SSL的中间人代理。

2
我认为“在代理服务器上具有HTTPS连接”并不意味着中间人攻击类型的代理服务器。我认为它是在询问是否可以通过TLS连接到HTTP代理服务器。答案是肯定的。

是否可以在代理服务器上拥有HTTPS连接?

是的,请参见我的问题和答案这里.

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

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


1
我曾尝试过:
  • 开始隧道: ssh -N -D 12345 login@proxy_server
  • 在 Firefox 设置中将代理设置为 localhost:12345
    • 并勾选“对所有协议使用此代理”

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

解决方法是:

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

参考自 digital ocean 文档

如何使用 SOCKS 隧道安全地路由 Web 流量而无需 VPN


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接