在得出任何关于 VPN 的结论之前,先打开开发者工具,加载页面,读一读请求头。你通常会看到一行 Accept-Language,请求一组特定的语言;这行来自浏览器设置,而不是你的出口地址。使用法语的用户在东京出口上仍然发送法语,而用法语应答的站点是在应答这行请求头。页面以熟悉的语言返回,并不代表隧道在泄漏。

先读请求头

在桌面浏览器中打开网络面板,重新加载,点开文档请求,查看请求头。在终端里不用浏览器也能看到同样的内容,还可以覆盖这个值来测试站点的行为:

curl -sS -D /tmp/h1 -o /tmp/b1 https://example.com
curl -sS -D /tmp/h2 -o /tmp/b2 -H 'Accept-Language: fr,en;q=0.6' https://example.com
grep -o 'lang="[^"]*"' /tmp/b1 /tmp/b2
diff /tmp/b1 /tmp/b2 | head -40

现在响应体就在磁盘上,比较的是页面本身,而不是一个可能根本没被发送的请求头。要看 html 标签的 lang 属性和可见文本,因为 Content-Language 是可选的,很多服务器会省略它。因此,一个在同一 URL 上把整页从英语换成法语的站点,在只比较请求头时可能看起来毫无变化。如果文档语言跟随请求头而出口保持不变,那么请求头就是决定性的输入,VPN 与这个观察无关。

这项测试要用第二个浏览器配置文件,而不是站点自带的语言切换器。切换器通常会写 cookie,那是另一项输入,最后你测到的会是 cookie 而不是请求头。

q 值意味着什么,又不意味着什么

RFC 9110 第 12.5.4 节把 Accept-Language 定义为应答所偏好的自然语言列表。每一项都可以带 0 到 1 的权重,不带权重的区间权重为 1,权重 0 表示该语言不可接受。这只是偏好顺序,仅此而已。

这个值不是位置报告,不是置信度评分,也不是身份。服务器可以忽略它,可以只提供它仅有的那一种译文,也可以让基于国家的重定向胜出。内容协商是服务器的选择,请求头是这项选择的输入,而不是命令。

当人们把本地化的页面当作位置测试时,漏掉的正是这一点。请求头说明这个人更愿意读哪些语言,而偏好可以随身去任何地方。

三项输入决定你实际得到哪种语言

从外部看不出哪一项胜出,所以要逐一隔离:

  • 请求头。 用设为另一份语言列表的第二个配置文件测试,观察在其他条件不变时页面是否改变。
  • 已保存的选择。 在没有 cookie 的隐私窗口中测试,或清除站点存储。账户设置和 localStorage 条目可以把语言固定好几个月。
  • URL 或重定向。 请求显式的语言路径,例如 /fr/,并观察基于源地址的重定向状态码。

按这个顺序做三项测试,记下哪一项改变了结果。开启 VPN 后语言改变的单次页面加载是弱证据,因为重定向和缓存中的 cookie 都能造成这种印象。

隧道改变了什么,又没改变什么

隧道改变服务器看到的源地址,也随之改变服务器从 IP 地理位置推断出的一切。它不会改写浏览器的语言偏好,不会清除已保存的设置,也不会改变你输入的 URL。这些输入原封不动,所以页面在出口移动之后仍可能保留旧语言。

地理位置还带来自己的噪声。地址数据库彼此并不一致,在一个国家注册的出口可能被映射到另一个国家,这会让基于国家的重定向因为与浏览器毫无关系的原因而显得错误。

一次快速检查可以把它们分开:重新请求页面,记下你呈现的地址,然后把应答语言和你的请求头列表作比较。如果语言与你的列表相符而不是与出口国家相符,那么请求头就是决定性输入,隧道里没有什么需要修的。

本地化的页面证明了什么,又证明不了什么

这里确实有一个隐私要点,而且比人们通常提出的要小。完整的语言偏好列表是浏览器指纹的一项输入,可能透露你是谁、读什么。这是真实的偏好披露,与证明流量逃出了隧道不是一回事。

本地化的页面不是泄漏指标。如果你真的想测试泄漏,请检查站点看到的出口地址、用于名称解析的解析器路径,以及 WebRTC 栈暴露的候选地址。这些测试才能回答页面语言回答不了的问题。

下次一个站点显得知道你的国家时,先打开请求头。如果应答语言与你的浏览器列表相符,那就没有任何泄漏,而你要看的层就是眼前这一层。

相关文章