中间人劫持代理
注:图片来源于 mitmproxy
本代理基于 TCP,是一个不关心协议的代理?
是的
HTTP 代理 VS Socks 代理
问题与挑战
获取远端主机名(Host)
当应用设置了代理后,在 TLS 协商前,应用首先会发一条 CONNECT 请求,期待通过代理和远端服务器建立 TLS 隧道(tunnel)。
1 | CONNECT 10.1.1.1:443 HTTP/1.1 |
作为中间人代理,并不帮助建立隧道,而是需要解析 CONNECT 请求,并从中获得远端服务器主机名。同时返回给被代理程序隧道建立成功的回复:
1 | // 根据请求指定版本号 |
Subject Alternative Name
这是服务器证书包含的一个字段,用于标识证书签发的域名。
通常服务器的域名保存在 Common name
字段,只保存一个域名,而 Subject Alternative Name
可保存多个域名,并支持通配符。也就是,有的时候访问的服务器主机并不和 Common name
匹配,但匹配 SAN
,这样的证书也是合法的。
服务器名称指示(SNI)
这是识别同一 IP 部署的多个带证书虚拟主机(Virtual Host)的技术。
基于名称的虚拟主机允许多个域名由同一IP地址上的单个服务器(通常为Web服务器)托管。使用 HTTP 时,其必须包含的协议头 Host
字段标识了欲访问的域名,据此分辨出虚拟主机。但是,由于 HTTPS 握手发生在服务器看到任何 HTTP 头之前,这时并不知道要访问哪个虚拟主机,也就不能把对应的服务器证书发给客户端,以完成 TLS 协商。
SNI 通过让客户端发送虚拟域名的名称作为TLS协商的一部分来解决此问题。
OpenSSL 客户端实现:
1 | // set host name before initiating the SSL connection |
Certificate Pinning
简单地说,就是某些程序(比如浏览器)只相信某些根证书签发的服务器证书。
通常证书通过证书链验证合法性,MyCert
由 IntermediateCert
签发,它又由 RootCert
签发,RootCert
已经内置在操作系统的可信任根证书中。 这样,MyCert
是合法的。常见内置的根证书有:Verisign, Digicert, Thawte等。
但是,Certificate pinning 的作用是,即使通过了上面的验证,如果不是某个指定 RootCert 签发的证书,也认为其不合法。这在一定程序上避免中间人攻击,但没有真的解决。
下面是一些使用 Certificate pinning 的应用:
- 微软的自动更新
- 苹果 App Store
- Twitter app
设置代理
通过 netsh winhttp
命令
设置代理:
1 | set proxy 127.0.0.1:8080 "<local>" |
通过 WinHTTP
接口设置
WinHTTP vs. WinINet
选择 WinINet 的原因:
- 支持 cookies
- Credential Cache (和SSL证书相关?)
- Credential Prompting (和SSL证书相关?)
INTERNET_PER_CONN_OPTION
INTERNET_PROXY_INFO
1 | INTERNET_PROXY_INFO proxy; |
设置自动代理
1 | wchar_t wzsPath[MAX_PATH]; |
1 | INTERNET_PER_CONN_OPTION_LIST List; |