Nginx-Caddy之HTTP-HTTPS代理区别

偶然的机会,发现 HTTP 代理和 HTTPS 代理这两个词,没接触过 HTTPS 代理,所以动手查了查,记录一下。

HTTP 代理

普通的 HTTP 代理是将浏览器发出的 HTTP 请求直接发送到代理服务器,然后代理服务器将 HTTP 请求进行解析,添加对应的源 IP(这里不考虑高匿代理),根据代理服务器的不同会对请求头进行不同的修改,然后将修改后的请求头和请求体一起发送到对应的服务器,然后等待服务器回复,将服务器返回的 HTTP 请求转发给客户端,完成代理任务。在这种情况下,HTTP 请求存在数据被代理服务器修改的情况,如果连接的是一个恶意的代理服务器,那么就可能存在数据泄露,隐私泄露等情况。使用 HTTPS 协议来访问对应服务器就可以避免这种问题。

为了确保数据的安全,HTTPS 请求的域名和端口都是加密的,所以代理服务器无法进行解析,也就无法修改对应的请求体,无法进行正常的 HTTP 转发任务。为了解决这个问题,就需要用到 CONNECT 方法。

  1. 当使用设置了代理服务器的浏览器发送 HTTPS 请求时,浏览器会先发送 CONNECT 包(未加密的)到代理服务器。
1
2
3
4
5
6
7
CONNECT www.microsoft.com:443 HTTP/1.0
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: www.microsoft.com
Content-Length: 0
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
  1. 代理服务器根据包中的内容,会在对应端口上和目标站点就建立一个 TCP 连接,建立成功后,会给客户端返回一个 HTTP 200 的状态码告知已连接成功。
1
2
3
4
5
6
7
HTTP/1.0 200 Connection Established
FiddlerGateway: Direct
StartTime: 11:56:22.008
Connection: close
EndTime: 11:56:22.538
ClientToServerBytes: 1416
ServerToClientBytes: 1358
  1. 之后浏览器会和目标网站进行正常的 HTTPS 握手请求,建立 TLS 隧道,并交换加密数据,代理服务器只会进行数据的转发,不会读取数据包中的内容,因为数据是加密的,所以想读取也是读取不了的。通过下图的抓包可以看出来,在进行了 TCP 三次握手之后,浏览器发送了 CONNECT 请求到代理服务器,之后代理服务器返回了 HTTP 200 的状态码告知已连接成功,然后浏览器开始和目标网站进行正常的 TLS 握手过程。

通过以上过程可以看到,通过代理服务器可以使 HTTP 和 HTTPS 请求都正常进行代理转发,而不需要客户端进行任何其他操作。

但是这里边有个问题在于 CONNECT 请求是明文的,如果在客户端和代理服务器之间进行中间人攻击的话,是有可能实现的。而且明文的请求可能会泄露客户端使用者的隐私数据,尤其是访问 HTTP 网站的情况下,还可能造成 Cookie 等认证信息的泄露,非常危险。

HTTPS 代理

HTTPS 代理是指 HTTP 代理支持 CONNECT 方法还是基于 SSL 的 HTTP 代理?我希望是后者, 但是一般情况下,我们所使用到的所看到的都是前者。

市面上的免费 HTTP/HTTPS 代理都只是 HTTP 代理支持 CONNECT 方法,并没有使用 SSL 隧道。也就是说基本上所有的代理服务器上标的 HTTPS 的意思仅仅是支持 HTTPS 流量,并不是真正的 HTTPS 代理。

基于 SSL 的 HTTP 代理解释起来其实很简单,跟 HTTPS 是一样的,这里的 HTTPS 指的是 HTTPS 协议,HTTPS 协议就是跑在 SSL 隧道上的 HTTP 代理,所以基于 SSL 隧道的 HTTP 代理就叫它 HTTPS 代理吧。有关 HTTPS 代理的具体说明或者说是官方定义,我只找到了类似的问题,没有找到官方的解释或是说明,有了解的小伙伴欢迎联系我。

通过这个链接可以得知 Chrome 是支持这种代理方案的,一般情况下 Chrome 代理都是通过 Chrome 拓展的方式进行设置的,如果没有拓展的话 Chrome 默认使用的是系统代理,系统代理据我所知是只支持 HTTP 代理的,所以我们这里使用 SwitchyOmega 这个拓展来设置 HTTPS 代理,然后测试 HTTPS 代理。

测试HTTP代理

由于 HTTP 代理比较简单并且在 HTTP 代理中已经抓过包了,所以这里就不进行测试了。

测试HTTPS代理

我们使用 SwitchyOmega 设置好 HTTPS 代理,由于一般的代理调试起来不是很方便,所以我们这里使用自己写脚本的方式来接收浏览器发出的流量,并且打印出来,还可以返回我们自定义的数据。

由于浏览器和服务器进行搭建 TLS 隧道需要证书,所以我们这里使用 ssl 这个库,加载我之前申请好的证书,当然了,域名也要提前准备好,然后将收到的数据打印出来,就可以看到浏览器通过 SSL 隧道发送的数据是什么了。

然后在服务器上将脚本跑起来,使用浏览器访问一个 HTTP 网站看看,浏览器端接收到了我们脚本所返回的数据。

服务器上的脚本也接收到了浏览器所发送的 HTTP 请求。

然后访问一个 HTTPS 请求试一下,由于我们使用脚本返回的数据不是浏览器预期接收到的数据,所以浏览器会报错,告诉我们网址无法连接。

在服务器上,我们可以看到浏览器发送给我们的 CONNECT 请求,这里由于浏览器默认会重试几次,所以有好几个请求。如果我们使用脚本回复一个正确的 200 请求的话,那么浏览器应该会继续发送后续的 TSL 握手包,进行正常的 HTTPS 握手。由于会增加脚本的复杂度,这里就不继续往下测试了。

同时我们使用 Wireshark 抓包也可以发现,在进行完 TCP 三次握手(红框)之后,继而进行了 TLS 握手请求,看不到 CONNECT 请求,所以我们基本可以确定,HTTPS 代理跑在 TLS 隧道之上的 HTTP 代理。

Nginx/Caddy搭建HTTPS代理服务器

测试完成了, 但是没有形成一个完成的闭环,由于 HTTPS 抓包的复杂性,我们通过测试脚本只是单向测试了 HTTPS 流量,没有正常访问到 HTTPS 的网站,所以我们这里使用 Caddy 来搭建一个测试服务器来验证 HTTPS 代理是否可以正常工作。

为什么不使用 Nginx 呢,因为 Nginx 默认不支持转发 CONNECT 请求,需要重新编译添加补丁才可以,这里为了方便不使用 Nginx 进行测试。

下载安装 Caddy 这里就不提了, 主要提示下使用 Caddy 进行 HTTPS 正向代理时需要添加 forwardproxy 插件,在下载 Caddy 时记得勾选插件。

关于配置文件的配置方法,可以见文档。我使用的 Caddy 的配置文件如下:

然后使用浏览器访问一个 HTTPS 网站,可以发现可以正常访问了,再次使用 Wireshark 抓包的结果发现和上一步测试 HTTPS 代理的结果是一样的。

解密SSL

这里使用 Caddy 我们看到了可以正常访问 HTTPS 网站,但是问题来了,我们怎么验证 TLS 隧道中跑的流量是 HTTP 代理的流量呢?虽然之前已经使用我们自己写的脚本测试过,但是我们换了 Caddy 当作代理服务器,所以为了严谨性我们还是证实下。我们使用 SSLKEYLOGFILE 这个文件来解密浏览器发出的 HTTPS 流量。SSLKEYLOGFILE 的话 Chrome 和 火狐我记得应该都是支持的。

配置好了 SSLKEYLOGFILE 的话 Chrome 和 火狐会自动将 TLS 隧道的解密密钥写入到定义好的文件中。我们可以在抓包软件中自动化解密请求。

首先将 SSLKEYLOGFILE 配置到环境变量中

然后使用浏览器访问一次 HTTPS 网站,打开我们指向的 log 文件,我们可以看到里边已经记录了很多的密钥,使用这些密钥就可以解密对应的 HTTPS 流量了。

我们使用 Wireshark 来完成这个任务。在首选项的 Protocols 中找到 TLS 选项,然后按照图中方法设置将路径指向到刚才保存的 log 文件中。

之后重启 Wireshark,开启抓包,使用浏览器再次访问 HTTPS 网站,然后看 Wireshark,可以发现在 TCP 三次握手之后是 TLS 握手,TLS 握手结束后是明文的 HTTP2 的请求,不再是加密的数据。进一步往下看的话,可以发现是使用 HTTP2 协议发送的 CONNECT 请求,确认是使用了 HTTP 代理,虽然是 HTTP2 的 HTTP 代理。

总结

通过对比和验证,可以了解到 HTTP 代理和 HTTPS 代理的区别,使用代理服务器和 CONNECT 方法,实现了转发 HTTP 流量和 HTTPS 流量。对于如何对猜想进行验证, 我们在这里首先使用自定义脚本的方式直接接收浏览器发出的二进制流量, 可以明显看出浏览器发出的数据的确是 HTTP 代理的流量,然后使用 Web 服务器的方式验证了 HTTP 代理的流量的确是跑在 TLS 隧道里,最后使用 Wireshark 解密 TLS 流量的方式确定了浏览器和 Web 服务器(Caddy)的确是通过 HTTP 代理的方式进行通信的,所以我们最终确定 HTTPS 代理就是跑在 TLS 隧道上的 HTTP 代理。

当然,解密 TLS 流量的方式不止这一个,还可以使用中间人的方式来解密,自签证书安装到系统中,然后使用两层代理的方式来转发流量到代理服务器,也就是说:浏览器流量 -> 本地抓包软件(安装好了证书)-> 流量转发软件 -> 代理服务器。这样的话实施起来会有点难度,同时也不是很好理解, 可能对我们的验证造成干扰,所以这里并没有使用这种办法。

最后解释下 TLS (HTTPS)是否真的安全?答案是否定的,从上面的中间人解密就可以看得出来,有不止一种可以解密 TLS 隧道中的数据,所以答案应该是相对安全,还是要日常防范中间人攻击。

以上就是本篇博客的全部内容。

注:以上分析流程及结果仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果与作者无关。

参考链接;

https://stackoverflow.com/questions/3794376/can-browsers-connect-to-a-proxy-over-ssl-tls

https://stackoverflow.com/questions/45874515/what-is-https-proxy

https://stackoverflow.com/questions/6594604/connect-request-to-a-forward-http-proxy-over-an-ssl-connection

https://juejin.im/post/5d317299f265da1bc14b6342

https://caddyserver.com/

https://caddyserver.com/docs/

本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接https://blog.d77.xyz/archives/6fb5f2d2.html