本文介绍 TLS 1.3 中的 Encrypted Client Hello (ECH) 扩展,如何解决 SNI 明文泄露隐私问题。

SNI 隐私问题

在 TLS 握手过程中,客户端发送的 ClientHello 消息包含 Server Name Indication (SNI) 扩展,用于告知服务器希望连接的域名。问题在于:SNI 是明文传输的,任何中间设备(如 ISP、代理、防火墙)都可以看到用户正在访问哪个域名。

1
2
3
4
ClientHello
├── SNI: example.com        ← 明文暴露!
├── supported_versions: TLS 1.3
└── cipher_suites: [...]

这意味着即使流量本身被加密,网络观察者仍能知道你访问了哪些网站。

ECH 解决方案

Encrypted Client Hello (ECH) 是 RFC 8446 的扩展,旨在加密 SNI 及其他敏感的 ClientHello 扩展。ECH 在 ClientHello 外层使用 ECH公钥 加密敏感的内部 ClientHello,只有目标服务器能解密。

工作原理

  1. DNS 配置:服务器在 DNS 的 HTTPS 记录中发布 ECH 公钥
  2. 客户端加密:客户端使用 ECH 公钥加密敏感的 ClientHello 信息
  3. 服务器解密:服务器用私钥解密,确认 ECH 隧道建立成功
  4. 握手继续:后续握手正常进行,SNI 已被加密
1
2
3
4
5
6
7
8
Outer ClientHello (明文)
├── SNI: ech.cloudflare.com      ← 加密通道入口
├── encrypted_client_hello: [...] ← 加密的有效载荷
└── ...

Inner ClientHello (加密)
├── SNI: example.com              ← 真正目标域名
└── ...

验证 ECH 支持

主流 CDN 提供商(如 Cloudflare)已支持 ECH。以下命令检查服务器是否支持 ECH:

1
2
3
# 使用 openssl 查看 ECH 支持
openssl s_client -connect cloudflare.com:443 -tls1_3 \
  -servername cloudflare.com 2>&1 | grep -i ech

实际输出示例:

1
2
ECH: ephemeral
ECH GAD: Yes

客户端启用 ECH

Chrome 和 Firefox 已默认启用 ECH 支持。验证方法:

  1. 访问 chrome://flags/#encrypted-client-hello 查看状态
  2. 访问 https://www.cloudflare.com/ssl/encrypted-sni/ 可测试 ECH 是否生效

DNS HTTPS 记录

ECH 公钥通过 DNS 的 HTTPS 记录(RFC 9460)分发。查看 cloudflare.com 的 HTTPS 记录:

1
2
# 使用 dig 查看 HTTPS 记录
dig +short HTTPS cloudflare.com

输出包含 ECH 公钥信息:

1
. alpn="h3" echconfig="AEX+..."

ECH 配置格式

ECH 公钥以 Base64 编码的 ECHConfigList 形式存储在 DNS 中:

1
2
3
4
5
6
7
echconfig = {
  version: 0xfe0d (ECH)
  kem: X25519
  cipher: HKDF-SHA256
  public_name: ech.cloudflare.com
  ...公钥参数...
}

服务器端配置

Nginx 配置(需要支持 ECH 的后端)

Nginx 本身不直接处理 ECH,ECH 终止通常由上游 CDN 或代理完成。如果你使用 Cloudflare:

1
2
3
4
5
6
server {
    listen 443 ssl http3;
    
    # Cloudflare 会自动处理 ECH
    # 无需额外配置
}

确保 DNS 正确配置 HTTPS 记录,CDN 提供商通常会自动处理。

自托管服务器配置

如果你需要自托管 ECH:

  1. 生成 ECH 密钥对
  2. 配置 DNS HTTPS 记录
  3. 在 TLS 握手时处理 ECH 扩展

使用 OpenSSL 可以生成 X25519 密钥(ECH 所需的 KEM):

1
2
3
# 生成 X25519 密钥对(用于 ECH)
openssl genpkey -algorithm X25519 -out ech-private.key
openssl pkey -in ech-private.key -pubout -out ech-public.key

ECH 安全性

防止的攻击

  • 网络监控:攻击者无法看到用户访问的具体域名
  • 流量分析:减少基于 SNI 的流量分类
  • ** censorship**:部分绕过基于 SNI 的审查

限制

  • 首包延迟:首次连接需要额外的 DNS 查询
  • 兼容性:旧客户端回退到明文 SNI
  • 部署复杂:需要正确的 DNS 配置

安全级别

ECH 提供与 TLS 加密同等级别的安全保障。即使攻击者拦截所有网络流量,也无法确定用户访问的具体域名。

验证 ECH 连接

使用 curl 测试

1
2
# 需要较新版本的 curl
curl -v --https-only https://cloudflare.com 2>&1 | grep -i ech

使用 Wireshark 观察

  1. 启动 Wireshark 并捕获 TLS 流量
  2. 筛选 tls.handshake.type == 1(ClientHello)
  3. 观察 SNI 扩展是否被加密

常见问题

Q: ECH 和 ESNI 有什么区别?

A: ESNI 是 ECH 的前身,已被 ECH 取代。ESNI 仅加密 SNI,而 ECH 加密更多 ClientHello 扩展,并解决了前向安全性问题。

Q: ECH 是否会影响性能?

A: 首次连接需要额外 DNS 查询,后续连接可缓存 ECH 配置。总体影响可忽略不计。

Q: 如何确认我的网站已启用 ECH?

A: 使用 https://www.cloudflare.com/ssl/encrypted-sni/ 或运行 openssl s_client 检查服务器响应中的 ECH 扩展。

总结

ECH 是 TLS 1.3 的重要扩展,解决了长期存在的 SNI 隐私泄露问题。通过加密 ClientHello 中的敏感信息,用户访问的域名对网络观察者不可见。主流 CDN 已支持 ECH,自托管用户也逐步部署中。

随着隐私需求增长,ECH 将成为 TLS 标配。


参考来源