WebRTC 候选列表究竟能说明 VPN 的什么
主机候选、服务器反射候选或中继候选只说明地址从哪来;真正决定媒体走向的,只有被选中的那对 ICE。
当某个 WebRTC 测试页打印出 172.20.4.15 时,它只是得知该网络中确实存在一个本地接口。这是真实的披露,但与「媒体从 VPN 出口离开」并不是同一个结论。候选发现与数据包路由是两套彼此独立的机制,而多数泄漏报告把它们合并成一个判断。你面对的是哪一种,决定了修复方式。
收集在任何数据发出之前就已完成
浏览器枚举本地接口,向 STUN 服务器发送绑定请求,并向 TURN 服务器申请分配,然后把结果作为候选行写入会话描述。这一切都在没有通话、没有媒体、也没有对端的情况下完成。因此候选列表记录的是发现过程,而不是传输过程。
现代浏览器可能把主机候选的字面地址替换成 mDNS 名称 .local,页面因此读不到私有 IP。本地网络仍然看得到解析该名称的多播查询,也就是说地址只是对应用隐藏,并没有从链路上消失。
顺序同样重要:重新加载页面会收集到新的一组候选,TURN 分配不同,反射映射也可能不同。两次结果不一致,未必意味着两套不同的配置。
三种候选,三类证据
每一行都来自不同的观测。每种类型回答不同的问题,表格按它们能证明什么、不能证明什么来区分。
| Candidate | Derived from | What it establishes | What it cannot establish |
|---|---|---|---|
| Host | An interface on the device | A local address exists in that network | That the peer can reach it |
| Server-reflexive | A STUN binding response | How a NAT mapped this source | Which exit the media will use |
| Peer-reflexive | A connectivity check that arrived | An address seen by the far side | That it was ever advertised |
| Relay | A TURN allocation | The relay server accepted the session | That the relay carries the call |
中继候选常被误当成用户的公网地址。它属于 TURN 服务器,它的存在只说明有中继可用,并不说明浏览器选择了它。主机候选上也有同样的误解,只是方向相反:mDNS 名称向页面隐藏了地址,而网络仍在传递那些查询。先读类型,再读取值。
被选中的那对才回答路由问题
ICE 会把每个本地候选与每个远端候选配对,对每一对执行连通性检查,然后提名胜出者。真正承载媒体的是被提名的那一对,它可以在浏览器自身的诊断信息中看到。列表里其余的一切都是落败的可能性。
胜出者告诉你路径的形状。中继到中继走 TURN 服务器,主机对留在本地网络,服务器反射对则直接穿过 NAT。这正是 VPN 检查所需的事实,而它与地址列表是两回事。两次候选列表完全相同的通话,也可能走不同的配对。
未被选中的候选同样被收集过,而收集本身就是离开本机的网络流量。最终使用了中继,并不代表在构建列表的过程中什么都没暴露。
控制项对应的是目标,而不是泄漏评分
WebRTC 的 IP 处理规范描述了几种策略:只使用承载默认路由的接口、使用所有接口,以及禁用未经代理的 UDP 以强制流量经过中继或代理。每一种都在连通性与延迟之间,换取不同形式的暴露。浏览器究竟实现哪一种、叫什么名字,随平台与版本而异。
按目标来选策略。向页面隐藏本地地址是一个目标,阻止直接的点对点流量是另一个,把每个数据包都留在隧道内是第三个。第一个主要关乎应用能知道什么,第二个关乎允许走哪条路,第三个还必须在平台或 VPN 的边界上强制实施。
无论选择哪种控制,都要验证两个部分:运行收集测试并记录候选类型,然后在真实会话中查看被选中的配对。一份干净的地址列表配上直连的媒体路径,依旧是你并不想要的路径。