这边文章中提到过虽然 TLS 能够加密整个通信过程,但是在协商的过程中依旧有很多隐私敏感的参数不得不以明文方式传输,其中最为重要且棘手的就是将要访问的域名,即 SNI(Server Name Indication)。同时还有用于告知客户端可用的应用层协议的 ALPN 拓展,泄露这个会导致攻击者知道服务器所支持的服务以及连接的用途。
之所以会暴露如此重要的内容,并不是设计时的失误,而是因为加密该内容会使得许多服务器无法正常工作。通常来说,一台服务器往往会提供许多服务,小到用 nginx 反向代理数个服务,大到 CDN 服务器同时为无数网站提供服务,而因为域名的不同,证书也不相同。那么当客户端访问服务的时候必须指明 SNI,服务器才知道应该返回哪个证书,但客户端收到证书前又没有办法加密,就陷入了一个先有鸡还是先有蛋的尴尬境地。ESNI 以及其后继 ECH 就是被设计出来弥补这一问题。
1、Encrypted SNI
ESNI(Encrypted SNI)正如其名,其设计的目标就是为了加密 SNI,是最先为解决这一问题提出的一个 TLS 拓展。虽然这一方案即将被废弃,但对于了解其后继者 ECH 有着极大的帮助。
ESNI 为了能够让客户端获得密钥发送密文,依赖于另一个服务 DNS 来分发密钥。客户端在使用 ESNI 连接服务器之前,会在常规的请求网站 IP 的 A/AAAA 请求上再进行一次 TXT 的请求以获得 ESNI 的公钥(实际是用 BASE64 编码后的公钥以及类似加密算法之类的参数),如下是请求本博客的结果:
$ dig _esni.crypto.dance TXT +short
"/wGuNThxACQAHQAgXzyda0XSJRQWzDG7lk/r01r1ZQy+MdNxKg/mAqSnt0EAAhMBAQQAAAAAX67XsAAAAABftsCwAAA="
客户端会将加密后的 SNI 以拓展的形式发送。服务器收到请求后就会用对应的私钥解密,并用解密后得到 SNI 进行后续的操作。解密失败则终止连接。
不过 DNS 请求本身是明文的,依旧存在风险,所以部署 ESNI 还需要引入 DoH(DNS over HTTPS)。DoH 除了保证可以加密传输公钥之外,还有一个重要的点就是可以认证 DoH 服务器,保证访问到的是正确的服务器,而非是(攻击者精心准备的)本地网络缓存,从而避免了本地缓存返回一个空的 TXT 记录以阻止客户端使用 ESNI 或者返回一个攻击者控制的密钥使得客户端使用了错误的公钥加密。但这一方法仍然存在问题:
- 首先就是密钥的分发,Cloudflare 在部署时每个小时都会轮换密钥,这样可以降低密钥泄露带来的损失,但是 DNS 有缓存的机制,客户端很可能获得的是过时的密钥,此时客户端就无法用 ESNI 继续进行连接。
- 其次是对网站的 DNS 请求可能返回有几个 IP 地址,每个地址分别代表了不同的 CDN 服务器,然而 ESNI 的 TXT 记录只有一个,可能会将该密钥发送给了错误的服务器导致握手失败。
2、Encrypted Client Hello
ECH(Encrypted Client Hello)出现的目标就是就是为了克服 ESNI 的缺陷,同时也正如其名,ECH 有着更大的野心,不单单加密 SNI,而是要加密整个 Client Hello。
ECH是 TLS 1.3 的草案扩展,它使客户端能够在 TLS 握手中加密其 client_hello,以防止泄露在正常 TLS 握手期间以明文形式发送的敏感元数据。ECH 最初被提议为 ESNI(加密服务器名称指示),因为服务器名称指示是握手过程中被动观察者可见的敏感字段之一,但后来更名,因为它涵盖了整个 Client Hello。 ECH 使用 HPKE(混合公钥加密)来导出共享密钥并加密 client_hello。
ECH 同样采用 DoH 进行密钥的分发,但是在分发过程上进行了改进。相较于 ESNI 服务器解密失败后终止连接,ECH 服务器会提供给客户端一个公钥供客户端重试连接,以期可以完成握手。
那么问题来了,解密失败服务器还是不知道 SNI 是多少,那么怎么返回公钥呢?
实际上 ECH 的 Client Hello 分为了两个部分,分别称为 ClientHelloOuter 和 ClientHelloInner。
- ClientHelloOuter 同普通的 Client Hello 消息一样,但删除了所有敏感元数据,只有连接必要的握手参数。
- ClientHelloInner:包含敏感信息,并使用 HPKE 加密,然后作为 ECH 扩展跟在 ClientHelloOuter 后面。
客户端发送外部问候,服务器使用 ECH 进行接收,并使用其 HPKE 密钥解密内部问候。
而服务器这边,握手实际上由 ECH 服务提供者来完成(称为 client-facing server)而非域名对应的背后的服务,由它来通知客户端解密是否成功以及发送正确的 ECH 公钥。
补充:ECH在wolfssl上已经支持了,见:Encrypted Client Hello (ECH) now supported in wolfSSL - wolfSSL
总结
ECH 是将会是 TLS 的又一次重大升级,这一次使得每次连接除了其发起者和接收者之外,没有人能够知道访问的是什么,有助于进一步保护用户隐私。尽管 ECH 还是一项进行中的工作,但随着工作的继续,显然我们离更安全的互联网又近了一步。
参考文档:ESNI 与 ECH 的前世今生 - ZingLix Blog
以下是Good-bye ESNI, hello ECH ! 译文
原文:https://blog.cloudflare.com/encrypted-client-hello/
现代 Internet 上的大多数通信都是加密的,以确保其内容仅对端点(即客户端和服务器)是可理解的。然而,加密需要密钥,因此端点必须就加密密钥达成一致,而不会将密钥透露给潜在的攻击者。这项任务使用最广泛的加密协议称为密钥交换,是传输层安全 (TLS) 握手。
在本文中,我们将深入探讨 Encrypted Client Hello (ECH),它是 TLS 的新扩展,有望显着增强这一关键 Internet 协议的隐私性。今天,TLS 连接的许多隐私敏感参数都是以明文方式协商的。这为网络观察者留下了大量可用的元数据,包括端点的身份、它们如何使用连接等。
ECH 对完整的握手进行加密,以便此元数据保密。至关重要的是,这通过保护服务器名称指示 (SNI) 免受网络上的窃听者的侵害来解决长期存在的隐私泄露问题。加密 SNI 很重要,因为它是给定客户端正在与哪个服务器通信的最清晰信号。然而,也许更重要的是,ECH 还为向 TLS 添加未来的安全功能和性能增强,同时最大限度地减少它们对最终用户隐私的影响奠定了基础。
ECH 是在 IETF 的推动下,学术界和技术行业领导者之间密切合作的产物,包括 Cloudflare、我们在 Fastly 和 Mozilla 的朋友(他们都是该标准的共同作者的附属机构)以及许多其他人。此功能代表了对 TLS 协议的重大升级,该协议建立在最先进的技术之上,例如 DNS-over-HTTPS,这些技术现在才逐渐成熟。因此,该协议尚未准备好进行互联网规模的部署。本文旨在作为通向完全握手加密的路标。
1、Background
TLS 的故事就是互联网的故事。随着我们对 Internet 的依赖程度不断增加,该协议也在不断发展以应对不断变化的操作要求、用例和威胁模型。客户端和服务器不只是交换密钥。他们协商各种各样的特性和参数:密钥交换的确切方法;加密算法;谁进行了身份验证以及如何进行身份验证;握手后使用哪个应用层协议;还有很多很多。所有这些参数都以某种方式影响通信通道的安全属性。
SNI 是影响通道安全性的参数的主要示例。客户端使用 SNI 扩展来向服务器指示它想要访问的网站。这对于现代互联网来说是必不可少的,因为现在许多原始服务器都位于单个 TLS 运营商后面很常见。在此设置中,操作员使用 SNI 来确定谁将对连接进行身份验证:没有它,就无法知道向客户端提供哪个 TLS 证书(域名不同,证书也不同)。问题在于 SNI 会向网络泄露客户端想要连接的原始服务器的身份,从而可能允许窃听者推断出有关其通信的大量信息。 (当然,网络观察者还有其他方法可以识别来源——例如,来源的 IP 地址。但是与同一 IP 地址上的其他来源共置使得使用此度量来确定来源比它只是简单地检查 SNI。)
虽然保护 SNI 是 ECH 的推动力,但它绝不是客户端和服务器协商的唯一隐私敏感握手参数。另一个是 ALPN 扩展,它用于在建立 TLS 连接后决定使用哪个应用层协议。客户端发送它支持的应用程序列表——无论是 HTTPS、电子邮件、即时消息,还是使用 TLS 进行传输安全的无数其他应用程序——服务器从该列表中选择一个,并将其选择发送给客户端。通过这样做,客户端和服务器向网络泄露了一个明确的信号,表明它们的能力以及连接可能用于什么。
有些功能对隐私非常敏感,以至于将它们包含在握手中是行不通的。已经提出的一个想法是使用password-authenticated key-exchange (PAKE)替换 TLS 核心的密钥交换。这将允许基于密码的身份验证与基于证书的身份验证一起使用(或代替),从而使 TLS 更加健壮并适用于更广泛的应用程序。这里的隐私问题类似于 SNI:服务器通常将唯一标识符关联到每个客户端(例如,用户名或电子邮件地址),用于检索客户端的凭据;并且客户端必须以某种方式在握手过程中将此身份传递给服务器。如果以明文形式发送,那么任何网络观察者都可以轻松访问此个人身份信息。
解决所有这些隐私泄露的一个必要因素是对握手加密,即除了应用程序数据之外,还对握手消息进行加密。听起来很简单,但这个解决方案带来了另一个问题:毕竟,如果握手本身就是交换密钥的一种方式,那么客户端和服务器如何选择加密密钥?(先有鸡、先有蛋?)当然,有些参数必须以明文形式发送,因此 ECH 的目标是加密所有握手参数,除了那些对完成密钥交换必不可少的参数。
2、Handshake encryption in TLS
为了理解 ECH 和支撑它的设计决策,稍微了解一下 TLS 中握手加密的历史会有所帮助。
在最新版本 TLS 1.3 之前,TLS 根本没有握手加密。在 2013 年斯诺登事件曝光后,IETF 社区(互联网工程任务组 Internet Engineering Task Force)开始考虑如何应对大规模监控对开放互联网造成的威胁。当 2014 年开始标准化 TLS 1.3 的过程时,其设计目标之一是尽可能多地加密握手。不幸的是,最终标准没有完全握手加密,包括 SNI 在内的几个参数仍然以明文形式发送。让我们仔细看看为什么。
TLS 1.3 协议流程如上图所示。只要客户端和服务器计算出新的共享密钥,握手加密就会开始。为此,客户端在其 ClientHello 消息中发送一个密钥共享,而服务器在其 ServerHello 中使用自己的密钥共享进行响应。交换了这些共享后,客户端和服务器可以导出共享秘密。每个后续的握手消息都使用从共享秘密派生的握手流量密钥进行加密。应用程序数据使用不同的密钥加密,称为应用程序流量密钥,它也是从共享秘密派生的。这些派生密钥具有不同的安全属性:为了强调这一点,它们用不同的颜色表示。
加密的第一个握手消息是服务器的 EncryptedExtensions。此消息的目的是保护服务器的敏感握手参数,包括服务器的 ALPN 扩展,其中包含从客户端的 ALPN 列表中选择的应用程序。密钥交换参数在 ClientHello 和 ServerHello 中未加密发送。
客户端的所有握手参数,无论是否敏感,都在 ClientHello 中发送。查看图 1,您可能会想到重新处理握手的方法,以便可以对其中一些进行加密,但可能会以额外的延迟为代价(即,通过网络进行更多往返)。然而,像 SNI 这样的扩展会产生一种“先有鸡还是先有蛋”的问题。
客户端不会加密任何东西,直到它验证了服务器的身份(这是 Certificate 和 CertificateVerify 消息的工作)并且服务器已经确认它知道共享秘密(Finished 消息的工作)。这些措施可确保密钥交换经过身份验证,从而防止中间人 (MITM) 攻击,在这种攻击中,对手以允许其解密客户端发送的消息的方式向客户端冒充服务器。因为服务器选择证书需要SNI,所以需要在密钥交换认证之前传输。
通常,只有在客户端和服务器已经共享加密密钥的情况下,才能确保用于身份验证的握手参数的机密性。但是这个密钥可能来自哪里?
TLS 1.3 早期的全握手加密。有趣的是,全握手加密曾被提议作为 TLS 1.3 的核心特性。在该协议的早期版本(draft-10,大约 2015 年)中,服务器会在握手期间为客户端提供一个长期有效的公钥,客户端将在随后的握手中使用该公钥进行加密。 (这个设计来自一个叫做 OPTLS 的协议,它又是从最初的 QUIC 提议中借用的。)被称为“0-RTT”,这个模式的主要目的是允许客户端在完成握手之前开始发送应用程序数据.此外,它还允许客户端对 ClientHello 之后的第一次握手消息进行加密,包括它自己的 EncryptedExtensions,这可能已用于保护客户端的敏感握手参数。
终此功能未包含在最终标准(RFC 8446,2018 年发布)中,主要是因为其增加的复杂性超过了它的实用性。特别是,它不会保护客户端获知服务器公钥的初始握手。初始握手的服务器身份验证所需的参数(如 SNI)仍将以明文形式传输。
尽管如此,该方案作为其他握手加密机制(如 ECH)的先驱而引人注目,它使用公钥加密来保护敏感的 ClientHello 参数。这些机制必须解决的主要问题是密钥分发。
3、Before ECH there was (and is!) ESNI
ECH 的直接前身是加密 SNI (ESNI) 扩展。顾名思义,ESNI 的目标是为 SNI 提供机密性。为此,客户端将在服务器的公钥下加密其 SNI 扩展并将密文发送到服务器。服务器将尝试使用与其公钥相对应的密钥来解密密文。如果解密成功,则服务器将使用解密的 SNI 继续连接。否则,它将简单地中止握手。这个简单协议的高级流程如图 2 所示。
对于密钥分发,ESNI 依赖于另一个关键协议:域名服务 (DNS)。为了使用 ESNI 连接到网站,客户端将在其标准 A/AAAA 上使用 ESNI 公钥查询 TXT 记录请求。例如,要获取 crypto.dance 的密钥,客户端将请求 _esni.crypto.dance 的 TXT 记录:
$ dig _esni.crypto.dance TXT +short "/wGuNThxACQAHQAgXzyda0XSJRQWzDG7lk/r01r1ZQy+MdNxKg/mAqSnt0EAAhMBAQQAAAAAX67XsAAAAABftsCwAAA="
base64 编码的 blob 包含 ESNI 公钥和相关参数,例如加密算法。
但是,如果我们只是要通过明文 DNS 查询将服务器名称泄露给网络观察者,那么加密 SNI 有什么意义呢?随着 (DoH) 的引入,以这种方式部署 ESNI 变得可行,它可以对提供 DoH 服务的解析器的 DNS 查询进行加密(1.1.1.1 是此类服务的一个示例。)。 DoH 的另一个重要特性是它提供了一个经过身份验证的通道,用于将 ESNI 公钥从 DoH 服务器传输到客户端。这可以防止源自客户端本地网络的缓存中毒:在没有 DoH 的情况下,本地攻击者可以通过返回空 TXT 记录来阻止客户端提供 ESNI 扩展,或者强制客户端使用带有密钥的 ESNI控制。
尽管 ESNI 向前迈出了重要一步,但它还没有达到我们实现完全握手加密的目标。除了不完整——它只保护 SNI——它还容易受到一些复杂的,这些攻击虽然很难实现,但指出了协议设计中需要解决的理论弱点。
ESNI 于 2018 年由 Cloudflare 部署并由 Firefox 在选择加入的基础上启用,这种体验暴露了依赖 DNS 进行密钥分发的一些挑战。 Cloudflare 每小时轮换一次其 ESNI 密钥,以便在密钥遭到破坏时最大限度地减少附带损害。 DNS 工件有时会缓存更长时间,其结果是客户端很有可能拥有过时的公钥。虽然 Cloudflare 的 ESNI 服务在一定程度上容忍了这一点,但每个密钥最终都必须过期。 ESNI 协议未解决的问题是,如果解密失败且无法通过 DNS 或其他方式访问当前公钥,客户端应如何处理。
依赖 DNS 进行密钥分发的另一个问题是,多个端点可能对同一源服务器具有权威性,但具有不同的功能。例如,对“example.com”的 A 记录的请求可能会返回两个不同 IP 地址之一,每个 IP 地址由不同的 CDN 运营。 “_esni.example.com”的 TXT 记录将包含这些 CDN 之一的公钥,但肯定不会包含两者。 DNS 协议不提供一种将对应于同一端点的资源记录自动绑定在一起的方法。特别是,客户端可能会无意中向不支持它的端点提供 ESNI 扩展,从而导致握手失败。解决此问题需要更改 DNS 协议。 (更多内容见下文。)
ESNI 的未来。在下一节中,我们将描述 ECH 规范以及它如何解决 ESNI 的缺点。然而,尽管存在局限性,但 ESNI 提供的实际隐私优势非常重要。 Cloudflare 打算继续支持 ESNI,直到 ECH 准备好投入生产。
4、The ins and outs of ECH(ECH的来龙去脉)
ECH 的目标是加密整个 ClientHello,从而通过保护所有隐私敏感的握手参数来弥补 TLS 1.3 和 ESNI 中留下的差距。与 ESNI 类似,该协议使用通过 DNS 分发并使用 DoH 获得的公钥在客户端的首次飞行期间进行加密。但 ECH 对密钥分发进行了改进:
- 使该协议对 DNS 缓存不一致的情况更加稳健。
- 如果解密失败,ESNI 服务器会中止连接,而 ECH 服务器会尝试完成握手并为客户端提供可用于重试连接的公钥。
但是,如果服务器无法解密 ClientHello,它如何完成握手呢?如图 3 所示,ECH 协议实际上涉及两个 ClientHello 消息:ClientHelloOuter,它像往常一样以明文形式发送;和 ClientHelloInner,它被加密并作为 ClientHelloOuter 的扩展发送。服务器仅使用这些 ClientHellos 之一完成握手:如果解密成功,则继续使用 ClientHelloInner;否则,它将继续处理 ClientHelloOuter。
ClientHelloInner 由客户端想要用于连接的握手参数组成。这包括敏感值,例如它想要到达的源服务器的 SNI(在 ECH 术语中称为后端服务器)、ALPN 列表等。 ClientHelloOuter 虽然也是一个成熟的 ClientHello 消息,但不用于预期的连接。相反,握手由 ECH 服务提供商本身(称为面向客户端的服务器)完成,向客户端发出信号,告知其由于解密失败而无法到达预期目的地。在这种情况下,服务提供商还会发送正确的 ECH 公钥,客户端可以使用该公钥重试握手,从而“更正”客户端的配置。 (这种机制类似于服务器在 TLS 1.3 早期为 0-RTT 模式分发公钥的方式。)
至少,两个 ClientHellos 都必须包含服务器验证密钥交换所需的握手参数。特别是,虽然 ClientHelloInner 包含真实的 SNI,但 ClientHelloOuter 还包含一个 SNI 值,客户端希望在 ECH 解密失败的情况下验证该值(即,面向客户端的服务器)。如果使用 ClientHelloOuter 建立连接,则客户端应立即中止连接并使用服务器提供的公钥重试握手。客户端不必在 ClientHelloOuter 中指定 ALPN 列表,也不必指定用于指导握手后行为的任何其他扩展。所有这些参数都被加密的 ClientHelloInner 封装。
这种设计解决了 - 我认为非常优雅 - 早期机制遇到的安全部署握手加密的大部分挑战。重要的是,ECH 的设计并不是在真空中构思出来的。该协议反映了 IETF 社区的不同观点,其发展与对 ECH 的成功至关重要的其他 IETF 标准相吻合。
- 第一个是重要的新 DNS 功能,称为 HTTPS resource record type.。在较高级别上,此记录类型旨在允许对同一域名具有权威性的多个 HTTPS 端点为 TLS 通告不同的功能。这使得依赖 DNS 进行密钥分发成为可能,解决了初始 ESNI 部署所发现的部署挑战之一。要深入了解这种新记录类型及其对 Internet 更广泛的意义,请查看 Alessandro Ghedini 最近关于该主题的博客文章。
- 第二个是 CFRG 的混合公钥加密 (HPKE) 标准,它指定了一个可扩展的框架,用于构建适用于各种应用程序的公钥加密方案。特别是,ECH 将其握手加密机制的所有细节都委托给了 HPKE,从而使规范更加简单和易于分析。 (顺便说一下,HPKE 也是 Oblivious DNS-over-HTTPS 的主要成分之一。)