原文地址:https://blog.cloudflare.com/encrypted-sni/
翻译水平有限,有不通顺的语句,请见谅。
原作者:Alessandro Ghedini 写于 24 Sep 2018

今天我们宣布支持加密SNI,这是TLS 1.3协议中的一项扩展,通过阻止包括ISP、咖啡店主和防火墙在内的路由窥视者拦截TLS服务器名称指示(Server Name Indication,SNI),防止用户访问的网站被获取,以此来改善互联网用户的隐私。

加密的SNI,以及Cloudflare免费提供的其他互联网安全功能,将使审查内容和跟踪互联网用户变得更加困难。想要了解它的工作原理,请继续阅读。

为什么需要SNI?

TLS服务器名称指示(Server Name Indication,SNI)扩展,最初在2003年标准化,允许服务器在同一组IP地址上托管多个启用了TLS的网站,方法是要求客户端在初始TLS握手期间指定要连接的站点。如果没有SNI,服务器将不会知道,该向客户端提供哪个证书、或者应用于哪个连接的配置。

客户端将包含其连接站点的主机名的SNI扩展添加到ClientHello消息中。在TLS握手期间它将ClientHello消息发送到服务器。遗憾的是,发送的ClientHello消息是未加密的,因为客户端和服务器在此时没有共享加密密钥。

TLS 1.3 with Unencrypted SNI
TLS 1.3 with Unencrypted SNI

TLS 1.3 with Unencrypted SNI

这意味着,路由上的观察者(比如,ISP、咖啡店主或防火墙)可以拦截到明文的ClientHello消息,并确定客户端尝试连接的网站。这使得观察者可以跟踪用户正在访问的站点。

但是使用SNI加密,客户端会加密SNI,即使其余部分的ClientHello是以纯文本格式发送的。

TLS 1.3 with Encrypted SNI
TLS 1.3 with Encrypted SNI

TLS 1.3 with Encrypted SNI

那么,为什么之前的原始SNI无法加密,现在就可以吗?如果客户端和服务器尚未协商加密密钥,那么加密密钥从何而来?

如果鸡必须先于蛋出现,那么鸡从何而来?

一些其他互联网功能一样,答案只有“DNS”。

服务器在众所周知的DNS记录上发布公钥,客户端可以在连接之前获取该公钥(就像它已经在A、AAAA或者其他记录所做的那样)。然后,客户端用“加密SNI”扩展替换ClientHello中的SNI扩展,该扩展是原始SNI扩展,但是使用服务器公钥导出的对称加密密钥加密,如下所述。然后,拥有私钥并且可以导出对称加密密钥的服务器可以解密扩展,从而终止连接(或者将其转发到后端服务器)。由于只有客户端和它所连接的服务器可以导出加密密钥,因此加密的SNI不能被第三方解密和访问。

请注意,这是TLS 1.3及更高版本的扩展,并不适用于以前版本的协议。原因很简单:TLS 1.3引入的一个更改(not without problems)意味着将服务器发送的证书消息移动到TLS握手的加密部分(1.3版本之前,它以明文形式发送)。如果没有对协议进行这种根本性的改变,攻击者仍然可以通过简单地观察网线上发送的明文证书来确定服务器的身份。

底层加密机制涉及使用Diffie-Hellman密钥交换算法,这种算法允许客户端和服务器可以在不可信的通道上生成共享加密密钥。因此,通过使用服务器的公钥(实际上是Diffie-Hellman半静态密钥共享的公共部分),以及由客户端在丢弃发送到服务器的ClientHello消息时生成的短小Diffie-Hellman共享的私有部分,加密的SNI加密密钥会在客户端进行计算。其他数据(例如客户端发送的 ClientHello 消息中的一些加密参数部分)也会混合到加密过程中以便进行更好的测量。

然后,客户端的ESNI扩展不仅包括实际加密的SNI位,还包括客户端的公钥共享,用于加密的密码套件以及服务器的ESNI DNS记录的摘要信息。另一方面,服务器使用自己的私钥共享和客户端共享的公共部分来生成加密密钥并解密扩展。

虽然这可能看起来过于复杂,但这可确保加密密钥以加密的方式绑定到为其生成的特定TLS会话,并且不能跨多个连接重用。这可以防止攻击者通过观察客户端发送的加密扩展,在单个的会话中只需抓取并将其重放到服务器,来取消屏蔽用户尝试连接的网站的身份(这称为“重放(cut-and-paste)“攻击​​”)。

但是,服务器私钥的协商会将从它生成的所有ESNI对称密钥处于危险之中(这将导致观察者解密先前收集的加密数据),这也是 Cloudflare 自己的 SNI 加密实施每小时轮换服务器密钥来提高前向保密性(forward secrecy)的原因,但会追踪前几个小时的密钥以允许DNS缓存和复制延迟,这样有稍微过时密钥的客户端仍然可以毫无问题地使用ESNI(但最终,所有密钥都会被丢弃并遗忘)。

但是等等,你确实真的是DNS吗?

细心的读者可能已经意识到,简单地使用DNS(默认情况下,是未加密的)将使整个加密SNI的想法完全没有意义:路由上的窥视者仍能够通过简单地观察,确定客户端连接到的网站。客户端自身发送的DNS查询是明文的,无论是否使用了加密的SNI。

但随着DNS功能,如 DNS over TLS(DoT)和 DNS over HTTPS(DoH),以及为其用户提供这些功能的公共DNS解析器(例如Cloudflare自己的1.1.1.1)的引入,DNS查询现在可以受到加密保护,免受审查人员和追踪器的偷窥。

然而,尽管来自 DoT(DNS over TLS) / DoH(DNS over HTTPS) DNS 解析器的响应可以在某种程度上受到信任(尽管存在恶意解析器),但坚定的攻击者仍有可能通过拦截与权威DNS服务器之间的通信,并注入恶意流量,来向解析器的缓存信息投毒。也就是说,除非权威的服务器和解析器都支持DNSSEC[1]。顺便提一下,Cloudflare的权威DNS服务器可以给返回给解析器的响应签名,1.1.1.1解析器可以验证它们。

IP地址又怎么办?

虽然现在DNS查询和TLS SNI扩展被保护,免受路径上攻击者的窥探,但仍然可以通过查看来自用户设备流量上的目标IP地址,来确定用户正在访问哪些网站。由于许多Cloudflare域名共享使用相同的地址集,因此我们的一些客户在某种程度上受到这一点的保护,但这还不够,需要更多的工作来更大程度地保护连接端的用户。请继续关注Cloudflare有关该主题的更多更新。

我在哪可以注册使用?

现在,使用我们的域名服务器,即可在Cloudflare的所有域名上免费启用加密SNI的功能,因此您无需在Cloudflare网站上执行任何操作即可启用它。在浏览器方面,我们在Firefox的朋友告诉我们,他们希望本周向Firefox Nightly添加加密SNI的支持(请记住,加密的SNI规范仍在开发中,因此它尚不稳定)。

通过访问encryptedsni.com网站,您可以查看您浏览体验的安全性。你正使用的是安全的DNS吗?您的解析器是否验证DNSSEC签名?您的浏览器是否支持TLS 1.3?您的浏览器是否加密了SNI?如果所有这些问题的答案都是“是”,那么您可以安心,因为您知道您的浏览不受窥探。

结束语

加密的SNI,以及TLS 1.3,DNSSEC和DoT/DoH,填补上了为数不多的能够在互联网上实现监控和审查的一个漏洞。实现无监控的互联网,还有更多的工作需要去做,但我们(在慢慢地逐渐)抵达那个目标。

[1]:需要注意的是,可以通过劫持DNS解析服务器和TLD服务器之间的BGP路由禁用DNSSEC。上周我们宣布了对RPKI的承诺,如果DNS解析器和TLD也同样启用了RPKI,这种类型的劫持将更加困难。