“HTTPS 代理”意味着两条 TLS 连接,而不是一条
代理这一跳与网站是两条独立的 TLS 关系,各有各的信任。先弄清是哪个端点验证失败,再改动任何证书设置。
“HTTPS 代理”这个说法只描述客户端与代理之间的传输,仅此而已。它并不表示后面的网站也由同一条连接加密,也不表示代理能读取页面。把这两条连接区分开之后,证书错误就不再神秘,与 VPN 的对比也变得具体。
两对端点,两次验证
在普通的隧道模式下,存在两条 TLS 关系。第一条从客户端连到代理,用于验证代理。第二条从客户端穿过隧道连到网站,用于验证网站。两者各自独立失败,也各自独立配置。
它们有各自的信任决策、各自的证书链和各自的错误信息。使用组织 CA 的代理与使用公共证书链的公共网站,本来就可能需要不同的验证配置,这并非配置错误。
把它们看成两条共用同一个套接字的连接,因为它们本来就是。
CONNECT 实际隧道化的是什么
客户端发送一个指明主机和端口的 CONNECT 请求,代理随即向该目标建立 TCP 连接。代理应答之后,后续字节被原样转发而不作解释,因此网站的 TLS 握手是原封不动地穿过代理的。于是代理只知道目标名称和连接元数据,在这种安排下它收不到解密后的页面。
这个区别正是隧道模式的全部安全论证,也是它的局限。
代理这一跳没有 TLS 的 HTTP 代理,依然可以通过 CONNECT 承载 HTTPS 网站。客户端与代理之间的交换是明文,而网站连接不是,把整件事叫作 HTTPS 代理恰好掩盖了这个差异。
是哪张证书失败了
验证失败时,先读错误里点名的那个端点,再去改任何信任设置。这两种失败在终端里看起来很像,指向的修复方法却完全不同,所以第一个问题永远是:哪条连接被拒绝了。
curl 正是为此把选项分开。代理 CA 文件配置的是对代理的验证,而 CA 文件配置的是对服务器的验证;适用哪种信任库行为还取决于 curl 的构建方式。
curl -v --proxy https://proxy.example.net:443 \
--proxy-cacert org-ca.pem --cacert public-ca.pem https://www.example.com/压制一项检查不是修复。它对任何一个端点都没有给出任何证明。
检查是另一种安排
TLS 检查不是隧道。做检查的中间人会终止面向网站的信任关系,并向客户端出示自己的证书,于是客户端验证的是中间人而不是网站。在经批准的设计下这可以是正当的,但它与逐字节转发是性质不同的主张。不要假定每个代理都会检查,也不要假定代理这一跳已加密就能排除它。
信任库里的组织 CA 只是提示你处于哪种安排,而不是证据。应当去问设计,而不是从证书链推断。
这两种安排对“谁能读取页面”这个问题的答案并不相同。
用同样的四行来比较代理和 VPN
只有把两种部署用同样的方式描述,这个比较才有用。
| Line | What to state |
|---|---|
| Protected segments | Which hops are encrypted, and between which endpoints |
| Verified identities | What each end proves, and against which trust anchor |
| Trusted intermediaries | Who can read or alter traffic by design |
| Traffic included | Which processes and destinations the policy actually covers |
为单个请求配置的代理只覆盖那个请求。它不会把设备上的每个进程都送进代理,示例命令正是这么窄。VPN 的覆盖范围取决于它的路由与应用策略,这同样需要查看而不是假定。两者都不会仅仅因为装上了就覆盖一切。
为每个选项写下这四行,争论通常就自行消解了。有用的比较不在于哪个标签听起来加密程度更高。它在于哪些区段受到保护、各自的端点验证了谁、你信任哪些中间人,以及策略实际覆盖了哪些流量。