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也需要显式开启,比如

scss 复制代码
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()

对应依赖是:

scss 复制代码
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 的系统解析器:

scss 复制代码
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 明确声明或按域名关闭:

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 ,说这个是最大的痛点。

相关推荐
Java后端的Ai之路4 小时前
06-React-Props
前端·react.js·前端框架
Flynt5 小时前
开源"间谍卫星"说数据全是真的,我把它的免key数据源挨个试了一遍
前端·开源·数据可视化
Csvn5 小时前
渲染管线
前端
程序员大阳5 小时前
副队长CSS教程(33)–圆角与透明
前端·css·透明·圆角
志尊宝6 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金6 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉6 小时前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化
IMPYLH6 小时前
HTML 的 <wbr> 元素
前端·html
Dovis(誓平步青云)7 小时前
常用命令藏得太深?怎么做一个快捷命令坞
运维·前端·自动化·音视频·prometheus
geovindu7 小时前
sql:Search-Optimized SQL Queries using mysql 9.0
android·数据库·sql·mysql·数据库开发·数据库架构