我们昨天宣布了Keyless SSL技术,并且得到了非常积极的反馈。我们翻查了博客、Reddit以及Hacker News上面的评论,发现大家似乎想了解更多的信息并且想要获取更深入的技术细节。所以在这篇博客里,我们将从技术细节的角度回答一些问题:Keyless SSL是如何设计的、如何工作的以及为什么它是安全的。在此之前,我们需要了解一些互联网上信息加密相关的一些背景知识,如果你对此很熟悉,可以直接跳过这些内容。

TLS

TLS是支撑互联网安全的主要技术。它允许网站向浏览器证明他们的身份,并且使用加密技术保护双方的信息交换。虽然TLS已经存在一些年头了,但是它对于很多人甚至对于科技发烧友仍然充满了神秘感。理解TLS基础原理是理解Keyless SSL的关键。

双重目标

TLS有两个主要的目的:保密和认证。他们对于互联网安全都至关重要。

当没有第三方可以理解两端的交流信息时,我们说这次通信是保密的。我们可以通过对称加密技术来提供保密性:两端都使用同一个没有其他人知道的密钥对各自发送的消息进行加密。在TLS中,这种对称性加密一般使用类似AES的强分组加密来完成。旧的浏览器和平台可能使用现在认为不安全的3DES或者RC4的流加密算法。

TLS的另一个目标是认证。认证是一种确认对端身份确实如其所称的方式,使用公钥技术来实现。网站使用证书和公钥密码学来向浏览器证明它们的身份。为了验证证书,浏览器需要做两件事情:证明对端是证书的所有者,以及证明证书是可信的。

一个网站证书包含一个公钥,网站可以通过证明它们拥有配对的私钥来表明自己是证书的拥有者。浏览器如果从可信的中心授权处发现证书可信,并且证书中包含所访问的域名,则认为访问可靠。更多的技术细节在关于我们开源的CFSSL工具包的博客中解释过。

在网络上,保密和认证通过建立共享的密钥以及验证证书的所有权来实现。TLS中将这个过程称之为握手。

握手细节

TLS协议是SSL协议发展而来的,而SSL协议是Netscape公司在20世纪90年代中期实现的。在1999年,IETF标准化了一个新的协议TLS,实际上这是SSL的一个更新版本。因为TLS和SSL如此相似,TLS 1.0使用了SSL协议的版本号3.1。这可能在刚开始的时候看起来让人困惑,但是这是有含义的,因为它本质上是SSL 3.0的一个小升级;并且TLS的后续版本也遵循了这个模式。另外,因为TLS是SSL的演进版本,所以TLS和SSL这两个术语在一些情况下是可以互换的。

TLS中主要有两种握手类型:一种是基于RSA的,另一种是基于Diffie-Hellman的。RSA和DH是现代密码学中的两个算法,并且普及甚广。这两种握手主要是的差别在于密钥的生成和身份认证是怎么实现的。


RSA握手和DH握手有着各自的优势和不足。RSA握手仅使用了一个公钥算法:RSA,而使用RSA认证的DH握手除了相同的RSA操作外,还有一个DH操作。假设采用RSA认证机制,那么RSA握手会更快。RSA和DH之类的公钥算法消耗大量的CPU,是握手中最慢的计算过程。一个笔记本电脑可能每秒仅执行数百此RSA加密,如果是进行AES运算,可能可以达到千万次每秒。

DH握手需要执行两个算法,但是它的优势是允许各端在没有私钥的参与下,生成(对称加密需要的)密钥。这给连接带来了“前向保密“特性,也就是说私钥泄露的情况下,之前的会话不会被破解。DH握手还支持非RSA的认证方式来提高性能,包括ECDSA。EC(Elliptic curves)可以使用更低的性能消耗来提供了相同程度的安全保障。使用ECDSA认证的DH握手比单纯使用RSA的握手可以更快。

CloudFlare支持上述两种握手方式,但是,就像待会我们会讲到的那样,握手类型是服务端选择的。CloudFlare会在能使用DH握手的情况下选择DH握手方式。

TLS名词解释

在我们继续探索握手的步骤之前,先熟悉一下这些定义。

1. Session key

Session key是握手的最终结果。它用于对称加密,使客户端和服务端在给对方发送消息前对消息进行加密。

2. Client random

客户端随机数是客户端生成的32字节的序列,在每个连接中都不相同,包含4字节的时间戳加上28字节的随机字节。最近Google chrome开始使用32字节的随机来防止客户端指纹。这些随机值通常叫做nonce。

3. Server random

服务端随机数与客户端随机数的区别仅在于它由服务端生成。

4. Pre-master secret

Pre-master secre是48字节的数据。它由客户端随机数和服务端随机数加上伪随机函数(PRF)生成。

5. Cipher suite

Cipher suite(加密套件)是组成TLS连接需要的所有算法的一个唯一的组合标志。它由这些算法构成:

* key生成(如DH或RSA)

* 身份认证(证书类型)

* 机密性(对称加密算法)

* 完整性(hash函数)

例如AES128-SHA代表:1)隐含使用RSA进行密钥生成;2)隐含使用RSA进行身份认证;3)128位AES+CBC保障机密性;4)160位SHA来保证完整性

一个更加严格的加密套件例子如ECDHE-ECDSA-AES256-GCM-SHA384代表:1)使用ECDHE进行密钥生成;2)使用ECDSA进行身份认证;使用256位AES+GCM来保障机密性;使用384位SHA来保障完整性。

了解了上面这些定义后,我们开始探索RSA握手吧。

RSA 握手


消息1:Client Hello

Client Hello包括客户端想要使用的协议版本和其他一些握手开始需要的信息包括客户端随机数和支持的加密套件。现代浏览器还会用SSNI机制添加要访问的域名。SNI机制可以让服务端在同一个IP上提供多个域名的服务。

消息2:Server Hello

收到client hello后,服务端从中取出握手需要的参数,并选择握手使用的加密套件。server hello消息包括服务端随机数,选择的加密套件以及证书。证书中包含了公钥和域名。

消息3:Client Key Exchange

在验证了证书和网站身份后,客户端创建一个随机的pre-master secret。这个密钥使用证书中包含的公钥进行加密后发送给服务端。

收到上述消息,服务端使用自己的私钥可以解密出pre-master secret。现在双方都用了pre-master secret以及客户端随机数和服务端随机数,使用这些数据双方可以生成相同的会话密钥。之后它们交换一个短消息向对方表明它们接下来将发送加密的消息。

DH握手


DH握手是TLS握手的另一种形式,它使用两个不同的机制:一个用于建立共享的pre-master secret,另一个用于身份认证。它主要的特性依赖与DH密钥协商算法。

在DH算法中,双方使用不同的密钥交换消息来获取一个共享的密钥。握手依赖于指数是可以交换的,对一个数先求a次方再求b次方和对一个数先求b次方再求a次方,结果是相同的。

这个算法的工作原理是:A拥有一个密码a,发送ga 到B;B拥有一个密码b,发送gb 到A;这样a和b就能计算出 gab

由于 gab 可能计算出非常大的数字,所以这个机制并不太好,并且我们可以很高效的计算出一个数的N次根。然而,我们可以改变问题空间然它可以正常工作,我们通过将计算结果用一个大素数取余来得到一个固定范围的数字,这也做取模。在模运算中取N次根被叫做离散对数问题,离散对数问题被认为是一个难题(所以无法有效破解)。

DH密钥协商算法还有一个使用椭圆曲线的变体,ECDHE。

现在我们看一下具体的握手流程:

消息1:Client Hello

类似RSA,client hello包含协议版本、客户端随机数、加密套件以及可选的SNI拓展。如果客户端支持ECDHE,还可包括支持的椭圆曲线列表。

消息2:Server Hello

服务端接收client hello后,提取出握手需要的参数,包括ECDHE包含的曲线。server hello信息包括服务端随机数,选择的加密套件以及服务器证书。

RSA和DH握手从这里开始使用不同的消息类型。

消息3:Server Key Exchange

为了开始DH密钥交换,服务端选择一些初始参数并且发送给客户端(对应上面我们提到的g^a)。服务端还需要证明它持有私钥,所以服务端为所有的消息计算出一个数字签名。参数和签名一起被发送到客户端。

消息4:Client Key Exchange

在验证身份后,客户端还会进行数字签名验证。之后客户端发送它产生的DH握手信息(对应上面我们提到的g^b)。

这时,两端都可以计算出pre-master secret(对应g^(ab)),加上客户端随机数和服务端随机数,它们可以计算出相同的会话密钥。之后它们交换一个短消息表明接下来的消息将被加密。

就像RSA握手,DH握手在交换Finised消息后正式结束,之后所有的通讯将被会话密钥加密。

去除本地密钥依赖

昨天我们宣布了Keyless SSL,也就是CloudFlare允许客户在不提供私钥的前提下仍然可以使用CloudFlare 服务的解决方案。

一旦安全通道建立,RSA握手将像下图一样工作:

DH握手的工作图示:

要达到上述的效果,需要对Nginx和OpenSSL进行修改,使私钥相关的操作能够远程且无阻塞的进行,这样Nginx可以在等待远程操作完成时处理别的工作。对Nginx和OpenSSL的修改以及CloudFlare和key server之间的协议都由iSEC和matasano进行审计。它们发现使用Keyless SSL的安全性与直接的SSL相当。Keyless SSL也被学术研究者从可证明的安全和性能的角度进行了研究。

key server可以在Linux或者其他UNIX操作系统(包括FreeBSD)以及windows服务器上运行。客户也可以获取C语言版本的参考实现,所以他们为自己编译一份兼容的key server软件。

Keyless SSL支持对一个相同的证书访问不同的多个key server。key server是无状态的,允许客户根据流量进行线性扩容。通过运行多台key server并且通过DNS进行负载均衡,可以达到高可用目标。

key server防护


为了使Keyless SSL安全,CloudFlare边缘到key server的连接也需要安全。key server可以为所有能够访问它的人提供私钥操作,就像一个密码数据库。保证只有CloudFlare可以访问key server来执行操作对Keyless SSL至关重要。

我们通过相互认证的TLS机制来保证CloudFlare和key server之间的连接安全。之前,我们描述的TLS握手只进行了客户端对服务端的单方向身份认证。在TLS双向认证中,客户端和服务端都由对方的证书并相互认证。

在Keyless SSL中,key server仅允许携带CloudFlare内部签发的证书的连接。我们使用我们自己签发的证书来进行双向认证。我们对这些证书由严格的控制,并且使用x509扩展的密钥使用选项来保证证书仅用于预期用途,这可以防止其他机构访问key server。客户也可以通过添加防火墙规则。

其他安全考虑

key server自身可以被修改使用硬件安全模块(HSM),这可以提供额外的安全保护,例如防止未经发现的类似Heartbleed的软件漏洞。

由于使用定长的应答,key server不会被类似Bleichenbacher一样的攻击所影响。旁路攻击例如timing攻击也无效,因为key server上的底层加密库对此免疫。

性能增强

CloudFlare为加速网站而生:如果使用CloudFlare,那么连接此网站应该更快。在Keyless SSL中也需要如此,使用CloudFlare的Keyless SSL应该比不使用CloudFlare快。有人问这怎么可能呢,毕竟Keyless SSL需要额外访问key server。答案在于地理分布。
CloudFlare的数据中心在全球20个国家分布,并且对于95%的互联网活跃用户,网络延迟都低于20毫秒。这让网络上的访问者使用离他们最近的CloudFlare服务器。访问者和CloudFlare之间的消息传输不用穿越很远的距离,所以连接延迟也更小。这种效应是CloudFlare加速网站的方式之一。
在Keyless SSL中,所有的访问者到CloudFlare之间的消息传输都是近距离的,唯一的远距离传输是访问key server。



省略握手

TLS提供了一个叫做session resumption也就是会话恢复的高性能特性。如果一个客户端曾经和服务端建立过连接并且它现在又要建立连接,他们可以使用简单握手。TLS中主要由两种机制可以实现会话恢复:session ID和sesion ticket。

session ID需要服务端保存会话状态(例如会话密钥)来用于会话恢复。session ticket则在首次握手时,会发送一个session ticket(可以认为是用ticket key加密的会话密钥)给客户端,如果客户端再次建立连接时携带这个加密后的会话密钥,服务端就可以解密它并且恢复会话。会话恢复的过程不需要使用私钥。

Firefox和Chrome是支持session ticket的主要浏览器。所有其他现代浏览器都支持session ID。大规模使用这些技术的唯一挑战是负载均衡。为了让服务器能够恢复会话,它需要持有之前建立的会话密钥。如果访问者去另一个服务器上建立连接,那台服务器就需要由方法获得原始的会话密钥。

会话恢复主要的问题在于无法大规模扩展到用了负载均衡的服务器上。如果客户端跟另一个服务器建立连接,旧无法恢复之前的会话。这并不是协议设计的失败,而是开源WEB服务器普遍缺失的一个功能。

Session ticket resumption

使用session ticket,我们可以在我们网络上的任意机器恢复会话。这依赖于我们即将开源的工程。

Twitter最近声明他们每12小时替换一次session ticket key。我们则每小时替换一次。我们建设了一个中心化的ticket key生成节点,每小时为我们分布在全球网络上的节点生成新的密钥。每个密钥的生存周期可以配置(默认是96小时),此后该密钥将被永久删除。为了分发这些密钥,我们在TLS层添加了到kyoto tycoon的访问,所以密钥的复制操作可以使用互相认证的TLS连接并且绑定到CloudFlare的CA。使用kyoto tycoon,ticket key可以在几秒内复制到我们每一个边缘机器。根据我们的开源理念,我们计划开源我们修改后的kyoto tycoon。每个服务器都得到ticket key之后,我们可以在我们整个网络上恢复TLS 会话。

Session ID resumption

我们同样也支持使用session ID从不同的机器恢复会话。与使用session tickets不同,我们只能恢复同一个数据中心内的TLS session,由于CloudFlare会将请求指向最近的数据中心,所以这对于99.99%的用户来说,也已经足够好了。浏览器并不会在不同的城市间频繁切换,所以大多数会话恢复请求发生在相同的数据中心。

我们可以使用ID去数据中心查找被缓存的会话密钥来恢复TLS会话。对于每一个新的连接,我们缓存一份使用session ID索引的加密过的会话密钥到数据中心。如果一个新的请求携带session ID,我们会去数据中心查找是否由相关的缓存。这些被缓存的会话密钥不会离开数据中心并且只会被缓存可配置的一定的时间长度,默认96小时。Session ID缓存让我们可以支持几乎所有尝试会话恢复的浏览器,不仅仅是Chrome或者Firefox。

其他的技术经验丰富的组织也会使用会话恢复。例如Google在它的基础架构中也使用相似的技术来恢复会话。

开源

在Keyless SSL的建设过程中,CloudFlare开发了大量的代码,并且已经将主要的工作开源回馈给社区:

Strict SSL: 支持Nginx对upstream的TLS验证,主要是为了验证key server的身份。修改已经合并到Nginx。http://mailman.nginx.org/pipermail/nginx-announce/2014.txt 

Session tickets:我们对Nginx进行了session ticket功能的支持。修改已经合并到Nginx。http://mailman.nginx.org/pipermail/nginx-devel/2013-October/004370.html 

CFSSL:我们最近开源了我们用于内部身份认证的工具,开源在Github。http://blog.cloudflare.com/introducing-cfssl/ 

Kyoto Tycoon:我们马上开源对Kyoto Tycoon的修改,它本身是一个高性能的key-value存储,现在可以支持双向认证。

Key Server:Keyless SSL的key server的参考实现已经开源在Github。https://github.com/cloudflare/keyless