Android 17 + OkHttp 5.5.0 ,全新 ECH 下你的 HTTPS 域名可以请求时被安全隐藏
在 Android 17 上目前官方主要增加了几个网络安全能力:Encrypted Client Hello、局域网权限隔离、默认证书透明度检查,这里面我感觉最有意思的是 Android 17 与 OkHttp 5.5.0 一起完成了 ECH 的落地。

这个的作用主要是补上了 HTTPS 一直以来存在的一个隐私缺口,过去 HTTPS 可以保护消息内容、账号密码、URL 路径和请求参数之类,但是隐藏不了用户正在访问哪个域名,而 Android 17 现在提供了系统级 TLS 与 DNS 接口,OkHttp 5.5.0 支持讲这些能力接入到 App 里面。
HTTPS 藏不聊域名,主要是因为当用户访问 https://bank.example.com/account/transactions 的时候,传统 HTTPS 会加密 /account/transactions、Cookie、请求正文和服务器返回内容,但 bank.example.com 对外部没做任何隐藏。
这里域名主要会从两个地方泄露:
-
第一个是 DNS 查询,设备需要先把
bank.example.com解析成 IP 地址,如果使用普通 DNS,查询会内容直接暴露在路由上,Android 的 Private DNS、DNS over TLS 和 DNS over HTTPS 可以处理这一层 -
第二个是 TLS 握手中的 SNI,现在大量网站共用 CDN 和 IP 地址,服务器需要在握手初期知道客户端要访问哪个域名,才能选择正确的证书和后端,这个域名长期以明文形式出现在
ClientHello中,也就是即便 DNS 已经加密,运营商依然可以从 SNI 恢复访问目标
而 ECH 就是对第二个泄露点下手,2026 年 3 月正式成为 RFC 9849 后,ECH 就可以将真实 SNI、ALPN 这些敏感握手字段放入加密的 ClientHelloInner,外部只保留一个面向 CDN 或 ECH 服务商的 ClientHelloOuter,链路上的观察者还是可以看到目标 IP、连接时间和流量大小,但是没办法从 TLS 握手直接读出真实域名。
它的隐私效果在大型 CDN 上特别明显,比如一个 IP 可能同时托管数千个域名,观察者只能判断设备正在连接 Cloudflare、Fastly 或其他服务商,但是没办法直观确认背后具体是哪一个网站。

所以核心就会 ECH 会加密了 TLS 握手中暴露真实域名的 SNI 等字段,配合加密 DNS,能同时封住 DNS 查询和 TLS ClientHello 这两个主要出口。
而实际对应 Android 17 提供了两类关键接口:
-
DnsResolver可以查询携带 ECH 配置的 HTTPS DNS Resource Record -
Conscrypt、SSLSocket和SSLEngine可以接收这份配置并执行 ECH 握手
开发者还可以通过 Network Security Configuration 中的
<domainEncryption>控制全局或单域名行为。
所以就算操作系统具备 ECH 能力,也还需要 App 使用的网络库主动接入,Android App 的 HTTPS 请求可能来自 OkHttp、Cronet、WebView、自研 C++ 网络栈或其他运行时,所以系统无法自动改写所有网络库的 DNS 和 TLS 流程。
这里 OkHttp 5.5.0 的意义就在这里,它承担了从发现 ECH 配置、选择连接地址到把配置交给 Android TLS 栈的全部工作:
| 阶段 | OkHttp 5.5.0 的工作 | Android 17 的工作 |
|---|---|---|
| DNS 查询 | 并行查询 A、AAAA 和 HTTPS RR | DnsResolver 提供原始 HTTPS RR 查询能力 |
| 读取配置 | 从 HTTPS RR 解析 echConfigList、ALPN、端口和 IP Hint | 通过 Network Security Policy 判断该域名是否允许 ECH |
| 选择连接 | 优先保留携带 ECH 配置的路由,避免无意回退到明文 SNI | 提供 IP、网络绑定和 TLS 能力 |
| TLS 握手 | 将 ECHConfigList 交给 Socket Adapter | SSLSockets.setEchConfigList() 执行 ECH |
| 服务端不支持 | 发出 ECH GREASE,让不同连接具有相似外观 | TLS 栈生成相应握手 |
| 配置过期 | 验证公开名称和服务端返回的新配置,再进行一次安全重试 | Conscrypt 返回 ECH 拒绝及重试信息 |
所以一条完整请求大致会经历这样的过程:
-
OkHttp 首先通过 DNS 获取 IP 地址,同时查询 HTTPS RR
-
HTTPS RR 中包含服务端的 ECH 公钥和相关元数据
-
OkHttp 把
ECHConfigList交给 Android 17 的 TLS Socket -
TLS 栈生成外层和内层两个 ClientHello
-
CDN 使用私钥解开内层握手,随后按照真实域名完成证书选择与连接路由
这里还有一个有意思的细节,那就是 OkHttp 5.5.0 的路由选择代码会检查 DNS 返回结果,只要存在携带 ECH 配置的可用路由,就排除同一批结果中没有 ECH 的路由。
这可以降低攻击者或错误配置诱导客户端静默降级到明文 SNI 的风险,服务端 ECH 密钥轮换后,如果客户端拿到过期配置,OkHttp 也会验证外层公开域名的证书,再接受服务端给出的新配置并重试,而不会对任意网络错误直接关闭 ECH。
所以 OkHttp 5.5.0 最大的改造其实发生在 DNS 层:
-
旧版 OkHttp 的
Dns.lookup(hostname)主要返回一组 IP 地址,适合查询 A 和 AAAA 记录,但是没有地方承载 HTTPS RR 中的服务元数据,而 ECH 公钥恰好发布在 HTTPS RR 的ech参数里,原有 API 很难接入 -
OkHttp 5.5.0 重新设计了 DNS API。新的
Dns.newCall()可以异步返回多种记录,结果里除了IpAddress,还增加了ServiceMetadata,其中包含:-
ECH 配置
echConfigList
-
服务端支持的 ALPN,例如 HTTP/2、HTTP/3
-
可覆盖的连接端口
-
IPv4、IPv6 地址提示
-
替代服务主机信息
-
这样 A、AAAA 和 HTTPS RR 查询会并行进行,然后通过新的内存缓存复用结果.
Jigsaw 对全球热门一万个域名的测量显示,超过 93% 的 HTTPS RR 和普通地址记录返回时间相差不超过约 50 毫秒,3.1% 的域名差距超过 100 毫秒,另有几十个域名始终没有正常返回 HTTPS RR。
另外,60% 的 HTTPS RR 能在 TCP 连接开始前返回,如果其中带有 IP Hint 或 HTTP/3 信息,客户端甚至有机会节省后续查询或握手时间。
这也解释了 OkHttp 为什么加入并行查询、异步流式结果和缓存,ECH 会增加一种 DNS 记录查询,但这部分开销通常可以与 A、AAAA 查询重叠。
不过就算升级到 OkHttp 5.5.0 以后,ECH也需要显式开启,比如
val bootstrapClient = OkHttpClient()
val dnsOverHttps = DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://1.1.1.1/dns-query".toHttpUrl())
.build()
val client = bootstrapClient.newBuilder()
.dns(dnsOverHttps)
.build()
对应依赖是:
implementation("com.squareup.okhttp3:okhttp:5.5.0")
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:5.5.0")
DoH 会同时加密 DNS 查询,同时默认读取 ECH 所需的 HTTPS RR,这样就可以覆盖完整的域名隐私链条:
DNS 查询不再明文暴露域名,TLS ClientHello 也不再明文发送真实 SNI。
另一种方式是 Android 17 的系统解析器:
val client = OkHttpClient.Builder() .dns(AndroidDns()) .build()
AndroidDns 会通过系统解析器获取 IP,并额外查询 HTTPS RR,这种更容易融入系统网络、VPN、按网络绑定和企业配置,但 DNS 查询是否加密取决于设备的 Private DNS 和当前网络环境。
OkHttp 的 Changelog 也明确提醒:使用
AndroidDns虽然可以启用 ECH,但是默认 DNS 路径不一定会经过加密,所以域名还可能在最初的 DNS 查询中泄露。
Android 17 对面向 API 37 的 App 默认使用 domainEncryption mode="enabled",这个模式下 DNS 提供 ECH 配置时强制使用 ECH,没有配置时发送 ECH GREASE,这里开发者可以继续通过 network_security_config.xml 明确声明或按域名关闭:
<network-security-config> <base-config> <domainEncryption mode="enabled" /> </base-config> </network-security-config>
所以实际部署至少要同时满足几个条件:
-
设备运行 Android 17
-
App 使用支持 ECH 的 OkHttp 5.5.0
-
DNS 能返回 HTTPS RR、服务端或 CDN 已经部署 ECH
不过有一个更大的问题是, OKHttp 不支持 KMP ,甚至不打算支持 KMP ,说这个是最大的痛点。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)