是否可以通过代理服务器进行 HTTPS 连接?如果是,什么样的代理服务器允许这样做?
9 回答
TLS/SSL(HTTPS 中的 S)保证您和您正在联系的服务器之间没有窃听者,即没有代理。通常,您使用CONNECT
通过代理打开 TCP 连接。在这种情况下,代理将无法缓存、读取或修改任何请求/响应,因此相当无用。
如果希望代理能够读取信息,可以采取以下方法:
- 客户端启动 HTTPS 会话
- 代理透明地拦截连接并返回一个临时生成的(可能是弱的)证书 K a,由客户端无条件信任的证书颁发机构签名。
- 代理启动 HTTPS 会话到目标
- 代理验证 SSL 证书的完整性;如果证书无效,则显示错误。
- 代理流内容、解密并用 K a重新加密
- 客户端显示东西
一个例子是 Squid 的SSL bump。同样,可以配置burp来执行此操作。这也被埃及 ISP 用于不太友好的环境中。
简短的回答是:这是可能的,并且可以使用特殊的 HTTP 代理或 SOCKS 代理来完成。
首先,HTTPS 使用 SSL/TLS,它通过在不安全的通道上建立安全的通信通道来确保端到端的安全性。如果 HTTP 代理能够看到内容,那么它就是中间人窃听者,这违背了 SSL/TLS 的目标。因此,如果我们想通过普通的 HTTP 代理进行代理,那么一定有一些技巧在发挥作用。
诀窍是,我们使用一个名为CONNECT
. 并非所有 HTTP 代理都支持此功能,但现在很多都支持。TCP 代理无法看到以明文形式传输的 HTTP 内容,但这不会影响它来回转发数据包的能力。这样,客户端和服务器就可以在代理的帮助下相互通信。这是代理 HTTPS 数据的安全方式。
还有一种不安全的做法,即 HTTP 代理成为中间人。它接收客户端发起的连接,然后发起另一个到真实服务器的连接。在实现良好的 SSL/TLS 中,客户端将被通知代理不是真正的服务器。因此,客户端必须通过忽略警告来信任代理才能正常工作。之后,代理只需解密来自一个连接的数据,重新加密并将其馈送到另一个连接。
最后,我们当然可以通过SOCKS代理来代理 HTTPS,因为 SOCKS 代理工作在较低级别。您可能认为 SOCKS 代理既是 TCP 代理又是 UDP 代理。
据我记得,您需要在代理上使用 HTTP CONNECT 查询。这会将请求连接转换为透明的 TCP/IP 隧道。
所以你需要知道你使用的代理服务器是否支持这个协议。
如果它仍然感兴趣,这里是一个类似问题的答案: Convert HTTP Proxy to HTTPS Proxy in Twisted
要回答问题的第二部分:
如果是,什么样的代理服务器允许这样做?
开箱即用,大多数代理服务器将配置为仅允许 HTTPS 连接到端口 443,因此具有自定义端口的 https URI 将不起作用。这通常是可配置的,具体取决于代理服务器。例如,Squid 和 TinyProxy 支持这一点。
这是我使用 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);
}
}
}
您可以使用中间人技术和动态 SSL 生成来完成此操作。看看mitmproxy——它是一个基于 Python 的、支持 SSL 的 MITM 代理。
通过 SSH 隧道(端口转发)HTTPS(Linux 版本):
使用 443 on 关闭
localhost
。以 root 身份启动隧道:
ssh -N login@proxy_server -L 443:target_ip:443
添加
127.0.0.1 target_domain.com
到/etc/hosts
.
您在本地主机上所做的一切。
然后:target_domain.com
可从localhost
浏览器访问。
我不认为“在代理服务器上有 HTTPS 连接”意味着代理服务器的中间人攻击类型。我认为这是在询问是否可以通过 TLS 连接到 http 代理服务器。答案是肯定的。
是否可以通过代理服务器进行 HTTPS 连接?
是的,请在此处查看我的问题和答案。HTTPS 代理服务器仅适用于 SwitchOmega
如果是,什么样的代理服务器允许这样做?
这种代理服务器部署 SSL 证书,就像普通网站一样。但是您需要一个pac
文件供浏览器通过 SSL 配置代理连接。
我试过
- 开始隧道:
ssh -N -D 12345 login@proxy_server
- 在 Firefox 设置中将代理设置为
localhost:12345
- 并勾选“将此代理用于所有协议”
但这会导致每当我尝试连接到 https 网站时出现错误“不安全的连接”。
解决方案是
- “取消勾选”“将此代理用于所有协议”
- 仅将代理“localhost:12345”设置为 SOCKS 代理
- 并将 HTTP 代理、SSL 代理、FTP 代理留空
来自数字海洋文档的参考