使用 TLS/SSL 加密你的 HTTP 代理
HTTP 代理是明文的,这导致实际访问的 URL 可以被他人监测到。即使使用 HTTPS 协议,经过 HTTP 代理时会发送 CONNECT
请求,告诉代理要连续到远程主机的指定端口。于是,访问的目标域名暴露了。
有没有办法将传输内容加密呢?比如像 HTTPS 那样,使用 TLS 协议连接到代理服务器,然后再进行 HTTP 请求。很遗憾的是,我在 ziproxy 的配置里没有发现这样的选项。在 shlug 邮件列表里询问后,Shell Xu 提到了 stunnel 这个工具。以前我试过用它把 HTTP 的网站转成 HTTPS 的,但是网站后端程序无法知晓用户实际上使用的是 HTTPS,有些郁闷,就没管它了。
这次再次请出 stunnel,在代理服务器上执行如下命令:
sudo stunnel -d 0.0.0.0:8081 -r localhost:8080 -p /etc/stunnel/stunnel.pem
这样,所有到服务器的 8081 端口的请求,都会经过 TLS 解密后传递给 8080 端口。同时响应的数据也会被加密后再返回请求方。
接下来的问题是,浏览器无法直接使用这种代理。实际上除了拿 openssl 命令手动连接外,我不知道任何程序能够使用这种代理。那好,本地弄个反过来加密/解密的服务好了。还是使用 stunnel。不过出了点意外:Arch Linux 的 stunnel 是第四版,不再用命令行参数,转而使用配置文件了。于是参考这篇 Upgrading to stunnel 4,写了份 stunnel4 的配置文件:
compression = zlib foreground = yes output = /dev/stdout client = yes pid = /tmp/stunnel.pid # or will output to syslog :-( output = /tmp/stunnel.log [name] accept = 8082 connect = server.com:8081
这样在本地 8082 端口监听,把所有请求加密后转发到 server.com 的 8081 端口。同时响应的数据会被解密后再返回。
现在,所有与代理服务器传输的数据都被加密了,不怕被偷窥啦。
后记:
后来,我发现其实代理服务器和我本机都装了两个版本的 stunnel,只是名字中不带版本号的一个是第三版而另一个是第四版而已……
再后来,我猛然想起神器 socat——这家伙是支持 OpenSSL 的!比如客户端这边像下边这样子就可以了:
$ socat tcp-listen:8082,fork openssl:server.com:8081,verify=0
socat 真是神器啊,cat、netcat、rinetd、stunnel 的功能都覆盖了!
这样使用的时候,每次来新请求时,socat 会 fork 一个新进程来处理。有点浪费资源。不过略微查看了下,stunnel 似乎也一样。