加密 DNS 的 ECS 到底传了什么:/24 与 /56 分别暴露你的位置

加密 DNS 的 ECS 到底传了什么:/24 与 /56 分别暴露你的位置

234
234
2026-10-24 / 0 评论 / 38 阅读 / 5686 字 / 正在检测是否收录...
加密 DNS 主机名:dns.23-4.cn | DoH 地址:https://dns.23-4.cn/dns-query | 配置教程 | 实时战报

用了加密 DNS,查询内容确实没人能看了。但你有没有想过:你的解析器把什么信息交给了下一跳?

这里有个不太被讨论的机制叫 ECS(EDNS Client Subnet)。简单说:你的解析器在向上游权威服务器查询时,可能附带上你的 IP 网段(通常是 /24,IPv6 是 /56),好让 CDN 返回离你最近的节点。

它不是泄露——因为接收方是你的解析器指定的上游,不是运营商。但它确实把你的位置信息往下游传了一层,而且大多数人不知道自己用的 DNS 传不传。

这篇给一套可执行的判定方法,以及一个反直觉的结论:加密不等于不带 ECS,带了 ECS 也不等于不安全。下面所有输出都是我在一台 Ubuntu 20.04(dig 9.18.30)上实际跑出来的。

一、先搞清楚 ECS 想解决什么问题

不带 ECS 时,权威服务器只能看到递归解析器的出口 IP。假设你的解析器在 A 地,你在 B 地,那么:

  • 权威服务器(比如某个 CDN)认为「请求来自 A 地」;
  • 于是返回了 A 地附近的节点;
  • 你实际在 B 地,被调度到了一个绕远的节点。

这就是「用公共 DNS 后访问变慢」的经典原因之一。

带 ECS 时,解析器会告诉权威服务器:「请求来自 1.2.3.0/24 这个网段」。权威服务器就能返回那个网段附近的节点。

代价:你的网段信息被传下去了。

二、怎么测一个 DNS 到底传不传 ECS

dig 有一个 +subnet 参数,可以携带 ECS 选项。如果服务端回显了这个选项,说明它处理了 ECS;如果不回显,说明它忽略了。

测本站的加密 DNS(DoT)

dig +tls +subnet=1.2.3.0/24 @dns.23-4.cn example.com | grep -iE "CLIENT-SUBNET|EDNS"

实测输出:

; EDNS: version: 0, flags:; udp: 1232

只有 EDNS 段,没有 CLIENT-SUBNET 行。 说明这个服务没有回显 ECS——它把我的 /24 丢掉了。

作为对照,换一个会回显的公共 DNS:

dig +subnet=1.2.3.0/24 @223.5.5.5 example.com | grep -iE "CLIENT-SUBNET|EDNS"

实测:

; EDNS: version: 0, flags:; udp: 1232
; CLIENT-SUBNET: 1.2.3.0/24/24

多了一行 CLIENT-SUBNET。

这一行值得拆开看:1.2.3.0/24/24 是三个字段:

字段值含义
第 1 段1.2.3.0网段地址(我传进去的)
第 2 段/24源前缀长度(我传进去的)
第 3 段/24作用域前缀长度(解析器决定往下传多少)

第三个数字最关键——它才是解析器「愿意暴露多少」的实际值。你传 /24,它可能只传 /24、也可能截短到 /16,甚至传 /0(等于不带)。

这个第三个数字,我实测出了它的真实作用

故意传一个更长的前缀,看它会不会被截短:

dig +subnet=1.2.3.4/32 @223.5.5.5 example.com | grep CLIENT-SUBNET

实测:

; CLIENT-SUBNET: 1.2.3.4/32/24

我传的是 /32(精确到单个 IP),但回显的第三个字段是 /24。

这说明解析器把我的 /32 截短到了 /24 才往下传。 它在保护我——即使客户端(或某个上游)想暴露精确 IP,它也只放行到网段级别。

再传一个更短的,看会不会被拉长:

dig +subnet=1.2.0.0/16 @223.5.5.5 example.com | grep CLIENT-SUBNET

实测:

; CLIENT-SUBNET: 1.2.0.0/16/24

我传 /16,回显是 /16/24。第三个字段还是 /24——但注意:第二个字段(我传的)是 /16,第三个(作用域)是 /24。

这两个数字的含义不同:

  • 第 2 段 /16 = 「我告诉你我在 1.2.0.0/16 里」;
  • 第 3 段 /24 = 「你可以按 /24 的粒度来对待我」。

作用域比源前缀更长(/24 > /16)是合法的——意思是「你可以假设我更精确到这个程度」。真正防止暴露的是源前缀:我传 /16,权威服务器就只知道 /16。

⇒ 关键结论:/32 会被截到 /24,但 /16 不会被拉到 /24。所以「传更短的」是安全的,「传更长的」要看解析器是否截断。

IPv6 同理:

dig +subnet=2001:db8::/56 @223.5.5.5 example.com | grep CLIENT-SUBNET

实测:

; CLIENT-SUBNET: 2001:db8::/56/64

传 /56,作用域是 /64。这说明 /56 是「源前缀」,/64 是「作用域」——和 IPv4 的 /24 是同一个位置的角色。

这就是判定 ECS 的可执行判据:同一个参数打两个 DNS,一个回显 CLIENT-SUBNET 一个不回显,差别一目了然,比看任何文档都直接。

三、/24 和 /56 分别暴露了什么

IPv4 的 /24 是 256 个地址;IPv6 的 /56 是 2^(128-56) = 2^72 个地址。

看起来 IPv6 的 /56 大得离谱,但实际上:

协议常见 ECS 长度相当于能定位到
IPv4/24256 个地址一个小区/一栋楼
IPv4/321 个地址你自己
IPv6/562^72 个地址一个运营商的一个片区
IPv6/642^64 个地址一个子网(通常一户)
IPv6/0—不暴露(等于关闭)

关键点:/24 和 /56 不是「一样安全」的两种选择,它们定位精度差得很远。

  • IPv4 /24 → 256 个地址,能定位到一个接入点;
  • IPv6 /56 → 虽然地址数巨大,但 IPv6 的分配策略通常按「一个片区一个 /56」,所以它同样能定位到一个片区。

所以规范(RFC 7871)给的建议是:ECS 应该使用 /24(IPv4)和 /56(IPv6)作为默认上限,不要传更长的前缀。但现实中也有解析器传 /32——那就是把你的确切 IP 交出去了。

想主动关掉,用 /0:

dig +tls +subnet=0.0.0.0/0 @dns.23-4.cn example.com | grep -iE "CLIENT-SUBNET|EDNS"

实测:

; EDNS: version: 0, flags:; udp: 1232

/0 的语义是「我的网段是 0.0.0.0/0」,等于告诉解析器「别带我的任何前缀」。

四、三个常见误解

误解 1:「用了加密 DNS 就没有 ECS 了」

不成立。 加密和 ECS 是两个正交的维度:

不带 ECS带 ECS
明文 DNS运营商能看到全部查询运营商能看到查询 + 你的网段
加密 DNS运营商看不到查询运营商看不到查询,但上游能看到你的网段

加密解决的是「链路上谁能看到」,ECS 决定的是「下游知道多少」。两者可以任意组合。

上面的实测就是证据:dns.23-4.cn 是加密的(DoT),且不带 ECS;223.5.5.5 是明文的,且带 ECS。但也存在加密且带 ECS 的服务,只是我没在这台机器上遇到。

误解 2:「ECS 是泄露」

不是。 泄露(leak)的定义是「查询到了你没授权的解析器」。ECS 里的接收方是你自己选的解析器的上游,是授权链路的一部分。

真正的区别是告知范围:

  • 不带 ECS → 上游只知道「某个解析器问了我」;
  • 带 ECS → 上游知道「某个解析器替 1.2.3.0/24 问我」。

这是精度问题,不是授权问题。把它叫「泄露」会让讨论失焦。

误解 3:「关掉 ECS 一定更好」

不一定。 关掉 ECS 的代价是调度精度下降:

  • 用 CDN 的站点,你可能被调度到远端节点,延迟上升;
  • 某些按地域做负载均衡的服务,会给你返回错误的节点。

所以选择是:

你的优先级建议
隐私优先(不想暴露任何位置)关 ECS(/0),接受可能的调度偏差
速度优先(CDN 命中率重要)保留 /24 / /56,这是规范推荐的上限
折中只在特定域名上带 ECS,其余关掉(需要解析器支持按域名配置)

⛔ 绝对不要用 /32 或 /128 ——那等于把你的确切 IP 交出去,没有任何性能收益能抵消这个代价。

五、自己验证的三条命令

# 1. 你的加密 DNS 传不传 ECS(回显 CLIENT-SUBNET 就是传)
dig +tls +subnet=1.2.3.0/24 @dns.23-4.cn example.com | grep -iE "CLIENT-SUBNET|EDNS"

# 2. 对照组(一个已知会回显的公共 DNS)
dig +subnet=1.2.3.0/24 @223.5.5.5 example.com | grep -iE "CLIENT-SUBNET|EDNS"

# 3. 主动关闭(/0)
dig +tls +subnet=0.0.0.0/0 @dns.23-4.cn example.com | grep -iE "CLIENT-SUBNET|EDNS"

怎么读结果:

  • 出现 CLIENT-SUBNET: x.x.x.0/NN/NN → 服务处理 ECS,看第三个数字判断实际传多长;
  • 只有 EDNS 没有 CLIENT-SUBNET → 服务忽略 ECS(你的网段没被传下去);
  • 报错或超时 → 该服务不支持 ECS 选项,或者你打错了端口。

验证做对了没有:第 1 条和第 2 条的结果必须不同。如果两条输出一模一样,说明你的 dig +subnet 根本没生效(老版本不支持这个参数),换个版本或者用 kdig。

六、为什么这件事值得单独讲

大部分人配加密 DNS 时只关心一件事:查询内容有没有被看到。这当然重要,但它只是隐私的一个维度。

完整的图景是三层:

  1. 链路层:谁能看到你的查询 → 由加密(DoT/DoH/DoQ)解决;
  2. 内容层:查询内容被记录多久 → 由服务商的日志政策决定;
  3. 元数据层:你的位置/网段信息传了多远 → 由 ECS 决定。

三层里最容易忽略的是第三层,因为它不体现在任何一个「安全」字样上,也没有任何界面会提示你。但它恰恰是唯一一条能直接把「一个 IP」关联到「一个地理片区」的信息。

而它的判定方法简单到只有一条 dig +subnet 命令——这就是为什么值得单独写一篇。

相关阅读

0个赞
取消