bekitty

我不会讲大道理,可我心里有一杆秤。 我看这个世界很苛刻,可我的心里很宽容。 我不需要权力与利益,可是我需要公平与正义。 我始终知道正道难行,可不得不行。

导航

第4章 传输层安全(TLS)

SSL协议最初是在Netscape浏览器中应用,用来保证通过Web进行电子商务交易的安全性,电子商务的安全性要求采用加密技术来保护客户的个人资料,以及认证用户,保证数据完整性,以确保交易安全。为了实现这一目标,SSL协议在应用层实现,直接在TCP层之上(
图4-1 ),使得在它上面的协议(HTTP,电子邮件,即时通讯,和许多其他应用)在网络操通信时,操作不变,同时提供通信安全。

如果正确的使用SSL,第三方只能推断连接的两端地址、加密类型,以及数据频率和发送的大概数据量,但无法读取或修改任何实际数据。

 

 

 

图4-1 传输层安全(TLS)

 

当SSL协议被IETF标准化时,它被更名为传输层安全(TLS)。许多人经常TLS和SSL名称互换使用,但在技术上,它们是不同的,因为各自描述了不同版本的协议。

SSL 2.0是第一个公开发布版本的协议,但由于发现了一些安全漏洞,它很快就被SSL 3.0取代。由于SSL协议是Netscape专有的协议,IETF制定了一个新的标准规范协议(RFC 2246),被称为TLS 1.0,最终升级到了TLS 3.0。

 

 

TLS和SSL 3.0之间的差别不是非常大,但还是有一些差别的,在TLS 1.0和SSL 3.0之间不能进行互操作。

 

 

-- TLS协议 - RFC 2246

 

TLS1.0在1999年1月正式发布,随后两个新的版本被发布,TLS1.1在2006年4月发布,TLS1.2在2008年8月发布,主要解决了一些安全漏洞以及扩展协议能力。虽然SSL 3.0内部实现和后续TLS版本非常相似,许多客户继续支持支持SSL 3.0和TLS 1.0 - 我相信有很好的理由升级到较新的版本,以保护用户免受已知的攻击!

 

TLS被设计成在一个可靠的传输协议如TCP之上运行。然而,它也被改编运行在数据报协议之上(如UDP)。RFC 6347中定义的数据报传输层安全(DTLS)协议就是基于TLS协议,在保留数据报的传输模式前提下,能够提供TLS类似的安全保证。

 

 

加密,身份验证和数据完整性

TLS协议的目的是为在其之上运行的所有应用提供三种服务:加密,身份认证,数据完整性。从技术上说,你不需要在了解任何细节的情况下使用这三个服务。您可能只接受证书,而不验证其真实性,但你应该清楚地意识到这样做的安全风险和影响。在实际应用中,一个安全的Web应用程序一般都会应用这三个特性。

加密 

一种用来混淆从一台计算机到另一个计算机之间信息的机制 

认证 

一种对提供的身份信息检验其合法性的机制 

完整性 

一种侦测信息是否被篡改和伪造的机制

为了建立一个安全的加密的数据通道,连接双方必须协商后续的加密算法,以及加密密钥。TLS协议制定了一个清晰的握手序列来进行这些交换,我们将在“TLS握手”章节进行详细讨论
。TLS在实际中应用广泛的一个主要原因是它巧妙的握手过程,握手过程使用了公共密钥加密(也称为非对称密钥加密)技术,通信双方可以在之前没有任何消息交互的情况下进行共享密钥协商,而且密钥的协商可以在未加密的通道上执行。

作为TLS握手的一部分,该协议还允许通信的两端互相验证对方的身份。应用到浏览器中,这个认证机制,允许客户端验证服务器是他们声称的身份(例如你的银行),而不是有人只是通过伪造的名称或者IP地址 - 这验证主要基于第三方证书颁发机构和信任链。此外,服务器还可以选择性地验证客户端的身份 - 例如,一个公司的代理服务器可以验证所有员工,每个人都可以有自己的公司签署的证书。

最后,除了用户认证和加密外,TLS协议还提供了它自己的消息分片机制,且采用消息认证码(MAC)对每一个分片消息进行签名。MAC算法是一个单向的哈希加密函数(一个非常高效的校验码),HASH函数密钥由两端进行协商。每当一个TLS记录被发送,生成的MAC值,将附加在该消息中,接收端采用相同的方法计算和验证所发送的MAC值,以确保消息的完整性和真实性。

三种方式结合起来,就构成了web上安全通信的基础。所有现代的Web浏览器都提供了各种加密算法,能够用来验证客户端和服务器,透明地执行每一条消息的完整性检查。

 

 

网络代理,中间服务器,TLS,web上的新协议

HTTP的成功和可扩展性创建了一个充满活力的生态系统,在这个生态系统中包含各种代理和中间服务器:高速缓存服务器,安全网关,web加速器,内容过滤器,和其他许多服务器。在某些场景下,我们都知道他们的存在(显式代理),而在很多情况下,这些代理对最终用户是完全透明的。

不幸的是,这些中间或者代理服务器也带来了一些小问题:一些代理服务器在不能解析的情况下可能只是转发HTTP的扩展,但是有一些代理服务器却盲目的去处理了数据,即使他们不应该处理。另外,一些安全服务器会错误的把正常流量当做恶意流量。

在实际应用中,如果不采用标准HTTP语义和80端口的做法,往往导致不可靠的网络部署:某些客户端不会有任何问题,而另一些可能会失败或者发生不可预知的行为 - 例如,在同一客户端可能在不同网络之间的切换时可能出现不同的结果。

由于这些情况的存在,新的协议和HTTP扩展,譬如WebSocket,SPDY,其他扩展,通常依赖于建立的HTTPS隧道,来绕过中间代理,并提供一个可靠的部署模型:加密隧道混淆了所有的数据,使中间服务器不可见。这解决了眼前的问题,但它有一个缺点就是是不能够充分利用中间服务器提供的服务,譬如:身份验证,缓存,安全扫描,等等。

这就是为什么大多数的WebSocket指南会告诉你使用HTTPS来传送数据到移动客户端 - 原因就是上面所述。随着时间的推移,中间服务器逐步升级,其可以识别出新的协议,HTTPS部署也将变得不那么必要了 - 除非您的会话实际需要TLS提供的加密,身份认证,完整性校验等功能!

 

 

TLS握手

在客户端和服务器开始基于TLS进行应用程序数据交换之前,双方必须先协商好加密的通信隧道:客户端和服务器必须协商好TLS协议的版本,加密套件,如有必要,还需要验证证书。不幸的是,每一个步骤都需要在客户端和服务器之间进行数据交互,这将增加了TLS连接的延迟(
图4-2 )。

 

 

 

图4-2 TLS握手协议

 

图4-2假设纽约和伦敦之间的单向传播延迟为28ms,在前面章节中讨论的TCP连接的例子一样-
见表1-1 。

 

0 ms

TLS运行在TCP基础之上,这意味着我们必须首先完成TCP 三次握手“ ,这需要一个完整的来回交互(RTT)。

56 ms

TCP连接建立后,客户端发送一些协商信息,如TLS协议版本,支持的密码套件的列表,和其他TLS选项。

84 ms

服务器挑选TLS协议版本,在加密套件列表中挑选一个密码套件,附带自己的证书,并将响应返回给客户端。可选的,服务器也可以发送对客户端的证书认证请求和其他TLS扩展参数。

112 ms

假设双方协商好一个共同的TLS版本和加密算法,客户端使用服务器提供的证书,生成新的对称密钥,并用服务器的公钥进行加密,并告诉服务器切换到加密通信流程。到现在为止,所有被交换的数据都是以明文方式传输,除了对称密钥外,它采用的是服务器端的公钥加密。

140 ms

服务器用自己的私钥解密客户端发过来的对称密钥,并通过验证MAC检查消息的完整性,并返回给客户端一个加密的“Finished”的消息。

168 ms

客户端采用对称密钥解密消息,并验证MAC,如果一切OK,加密隧道就建立好了。应用程序数据就可以发送了。

 

一个新的TLS连接需要两个往返“完整的握手”。或者,可以使用“简短握手”,这只需要一个来回-参见“TLS会话恢复” 。

协商一个安全TLS通信隧道是一个复杂的过程,很容易犯错。好消息是,上述复杂的工作都已经由服务器和浏览器替我们做了,所有我们需要做的是提供和配置证书。

尽管浏览器和服务器替我们完成了复杂的工作,当我们的Web应用程序没有必要进行加密通信时,我们需要牢记每TLS连接将需要额外的两个TCP往返 - 在应用程序可以进行交换数据前,这是一个比较长的延迟!如果管理的不好,通过TLS传输数据可能增加几百ms的延迟,当然不太可能达到上千ms的延迟。

 

 

公钥加密 vs 对称加密

公钥加密仅用于会话创建TLS隧道的过程中。服务器提供公钥给客户端,客户端产生一个对称密钥,采用服务器的公钥加密,加密后的对称密钥,返回给服务器。然后,服务器用自己的私钥进行解密对称密钥。

使用由客户端生成的共享密钥的对称密钥加密法将其用于后续客户端和服务器之间的所有通信。这么做的主要原因是为了提升性能 - 非对称加密算法的计算复杂度高很多。想理解这个差异,如果您的计算机上安装了OpenSSL,你可以运行下面的测试:

 

  • $> openssl speed rsa
  • $> openssl speed aes

需要注意的是两次测试之间无法直接对比:RSA测试通过对不同长度的密钥进行一组数据进行测试,而AES性能测量的是每秒加密的字节数。尽管如此,它应该很容易地看到,推荐的1024或2048位密钥长度的RSA运算(全TLS握手),可能成为瓶颈。

确切的性能受使用的硬件,CPU核数量,TLS版本,服务器的配置,和其它因素的影响。不要受市场宣传误导,也不要相信过时的基准测试!始终在自己的硬件上做性能测试。

 

 

应用层协议协商(ALPN)

两个网络节点可能使用一个自定义的应用程序协议进行互相通信。解决这个问题的方法之一是在确定协议的前期,给它分配一个众所周知的端口(例如,端口80用于HTTP,TLS的端口443),并配置所有客户端和服务器使用它。然而,在实践中,这是一个缓慢和不切实际的过程:每个端口的分配必须批准,更糟的,防火墙及其他中间服务器往往只允许使用80和443进行通信。

结果是,为了我们自定义协议更加容易部署,我们必须复用80或443端口,通过额外的机制来协商应用协议。80端口被保留用于HTTP,HTTP规范提供了一个特殊的Upgrade流程来完成这个目标。然而,使用Upgrade可能带来额外的网络往返延迟,并在实际应用中往往因为许多中间服务器的存在是不可靠的,- 参考“代理,中间服务器,TLS协议和网络上的新协议“章节。

 

HTTP升级流程的一个例子可以参考“高效的HTTP 2.0升级和发现”章节 。

解决的办法是,使用端口443,这是保留给HTTPS会话(运行于TLS之上)。端到端的加密隧道使数据加密通过各种中间服务器,而不被篡改,这可以快速和可靠的方式实现和部署任意的应用程序协议。然而,使用TLS解决了可靠性,我们仍然需要一种方式来协商应用协议!

HTTPS会话,当然,复用了HTTP的升级机制来执行协商,但是这带来了一个额外完整的往返延迟(RTT)。如果我们把协议协商作为TLS握手本身的一部分呢?

正如它的名字所暗示的,应用层协议谈判(ALPN)是一个TLS扩展,支持在TLS握手( 图4-2 )过程中进行协议协商,从而省去通过HTTP升级流程所需的额外往返延迟。具体地,过程如下:

 

  • 客户在ClientHello消息添加新的ProtocolNameList字段,包含支持的应用程序协议列表。
  • 该服务器检查ProtocolNameList字段,并在ServerHello消息中返回一个ProtocolName字段,用来指示服务器端选择的协议。

服务器可能只响应其中一个协议,如果它不支持任何客户端要求的协议,那么它可能选择中止连接。其结果是,TLS握手完成后,安全隧道建立好了,客户端和服务端也协商好了所使用的应用协议 - 它们可以立即开始通信。

 

ALPN节约了HTTP升级交换的必要性,节省了一个额外的往返延迟。然而,请注意,TLS握手本身依旧必须被执行,因此ALPN协商不会比采用HTTP升级流程建立的未加密的通道快。相反,它只是确保通过TLS进行应用协议协商不会更慢 。

 

 

NPN和ALPN的历史关系

NPN(Next Protocol Negotiation)是一个TLS扩展,是Google提供的SPDY中的一部分,以实现在TLS握手中进行应用协议协商。听起来是不是很熟悉?最终的结果是和ALPN功能相当。

ALPN是NPN扩展的修订和IETF批准版本。在NPN中,服务器宣告它支持的协议,然后由客户端选择并确认协议。在ALPN,这个过程正好倒过来,客户端宣称其支持的协议,服务器选择并确认协议。改变的理由是,ALPN可以和其他比较接近的协商标准保持最大程度的一致性。

换句话说,ALPN是NPN的继任者,NPN已被弃用。依赖于NPN协商的客户端和服务器必须进行升级,使用ALPN。

 

 

SNI(Server Name Indication)

任意两个TCP端之间都可以建立加密的TLS隧道:客户端只需要知道对端的IP地址就可以建立连接,并执行TLS握手。但是,如果服务器需要部署多个独立的网站,每个与自己的TLS证书,但使用同一个IP地址 - 请问如何处理?棘手的问题,答案是不行。

为了解决上述问题,SNI(服务器名称指示)扩展被引入到TLS协议中,它允许客户端在握手开始指示他想要连接的主机名。服务器检查SNI主机名,选择适当的证书,并继续握手。

 

 

TLS,HTTP和专用IP

TLS + SNI工作流程和HTTP的Host头域宣告流程是相同的,客户端在头域中指示它要请求的Host:同一IP地址可能会部署许多不同domain,SNI和Host都是用来区分不同的Host或者Domain。

不幸的是,许多老的客户端(例如,大多数在Windows XP上运行的IE浏览器,Andr​​oid 2.2,和其他一些浏览器)不支持SNI。因此,如果您需要为这些老客户提供TLS,那么你可能需要为每个主机的专用IP地址。

 

 

TLS会话恢复

完整的TLS握手需要额外延迟和计算,为所有需要安全通信的应用带来了严重的性能损耗。为了帮助减少一些性能损耗,TLS提供恢复机制,或多个连接之间共享相同的协商密钥数据。

 

 

会话标识

“会话标识符”(RFC 5246)恢复机制在SSL 2.0中首次被引入,它允许服务器在“ServerHello消息”中构建和发送一个32字节的会话标识符,作为“ServerHello”消息的一部分。

在服务器内部,服务器维护一个会话ID和其对应的协商参数的缓存。反过来,客户端也同时存储会话ID信息,在后续的会话中,可以在“ClientHello”消息中携带session ID信息,指示服务器它保存了session ID对应的密钥和加密算法等信息,并且可以重用这些信息。假设在客户端和服务器都能在它们各自的缓存中找到共享的会话ID参数,那么缩减的握手(
图4-3 )就可以进行了。否则,开始一个新的会话协商,这将产生一个新的会话ID。

 

 

 

图4-3 缩减的TLS握手协议

借助会话标识符,我们能够减少一个完整的往返,以及用于协商的共享密钥的公钥加密算法开销。这让我们能快速的建立安全连接,而不损失安全性。

 

在实际应用中,大多数Web应用程序试图通过建立到同一个主机的多个连接并行获取资源,这使得会话恢复成为必不可少的一个优化项,其可以减少延​​迟,计算成本。

大多数现代浏览器都会有意的等待第一TLS连接完成后,再打开到同一台服务器的新连接:后续TLS连接,可以重复使用的SSL会话参数,以避免握手的延迟和损耗。

然而,“会话标识符”机制的一个限制就是要求服务器为每个客户端创建和维护一个会话缓存。这会为服务器上带来几个问题,对于一些每天同时几万,甚至几百万的单独连接的服务器来说:由于缓存session ID所需要的内存消耗将非常大,同时还有session ID清除策略的问题。这对一些流量大的网站来说不是一个简单的任务,理想的情况下,使用一个共享的TLS会话缓存可以获得最佳性能。

上述问题没有是不可能解决的,许多高流量的网站成功的使用了会话标识符。但是,对任何多服务主机的部署,会话标识符方案需要一些认真的思考和好的系统架构,以确保良好的的会话缓存。

 

 

Session Tickets

为了解决上面的会话缓存带来的服务器部署问题,“Sesion Ticket”(RFC 5077)取代机制被引入,目标是消除服务器需要维护每个客户端的会话状态缓存的要求。相反,如果客户指示它支持Session Ticket,在TLS握手的最后一步中服务器将包含一个“New Session Ticket”信息,包含了一个加密通信所需要的信息,这些数据采用一个只有服务器知道的密钥进行加密。

这个Session Ticket由客户端进行存储,并可以在随后的一次会话中添加到 ClientHello消息的SessionTicket扩展中-因此,所有的会话信息只存储在客户端上,Session Ticket仍然是安全的,因为它是由只有服务器知道的密钥加密的。

Session Identifiers和Session Ticket机制通常分别被称为“会话缓存”和“无状态恢复”机制。无状态恢复的主要改进是消除服务器端的会话缓存,从而简化了部署,它要求客户在每一个新的会话开始时提供Session Ticket - 直到Ticket过期。

 

在实际应用中,在一组负载平衡服务器中部署Session Ticket,也需要仔细考虑:所有的服务器都必须用相同的会话密钥,或者可能需要额外的机制,定期轮流在所有服务器上的共享密钥。

 

 

信任链和证书颁发机构

身份验证是建立每个TLS连接一个重要的组成部分。毕竟,TLS可以与任何端通过一个加密的隧道进行通信,包括攻击者,除非我们可以确信和我们通信的对方是可信任的,不然所有的加密工作都是无效的。要了解我们如何验证对方的身份,让我们来看看一个Alice和Bob之间简单的身份验证工作流程:

 

  • Alice和Bob生成自己的公钥和私钥
  • Alice和Bob都各自隐藏自己的私钥
  • Alice和Bob各自向对方分享自己的公钥
  • Alice向Bob发送一个新的消息,并用她的私钥签名
  • Bob使用Alice的公钥来验证消息签名

信任是上述通信的一个重要组成部分。具体来说,公钥加密算法允许我们使用发送方的公钥来验证其用私钥签名的消息,但这个验证是基于信任的基础上。在上面的交换过程中,Alice和Bob可以亲自交换他们的公钥,因为他们互相了解对方,他们确定他们的交换没有被第三方冒名顶替 - 也许他们甚至通过另一种方式来验证他们的身份,秘密(物理)握手,通过他们已经建立的握手!

接下来,Alice从她从未见过的Charlie那里接收到一条消息,但Charlie声称自己是Bob的朋友。事实上,Chralie为了证明他是Bob的朋友,Charlie请求Bob用他的私钥对自己的公钥进行签名,并在消息中附带了这个签名(
图4-4 )。在这种情况下,Alice首先检查Bob对Charlie公钥的签名。她知道Bob的公钥,因此她可以确认Bob确实对Charlie的公钥签名了。因为她信任Bob对Charlie的签名,她接受该消息,并対Charlie的消息进行类似完整性检查,以确保它确实是从Charlie发送过来的。

 

 

 

图4-4 Alice,Bob和Charlie的信任链

我们刚才所做的过程就是建立信任链:Alice信任Bob,Bob信任Charlie,信任被传递,Alice决定信任Charlie。只要信任链中没有人被攻破,我们能够构建和扩大信任的名单。

web和您的浏览器上的身份认证遵循了上面同样的步骤。这时你可能会问:你的浏览器该信任谁,当你使用浏览器是该信任谁?这个问题至少有三个答案:

手动指定的用户证书 

每一个浏览器和操作系统都提供了手动导入任何您信任的证书的机制。如何获得证书,并验证其完整性完全取决于你。 

证书颁发机构 

证书颁发机构(CA)是一个值得信赖的第三方的机构(所有者),其证书值得信任。 

浏览器和操作系统 

每个操作系统和大多数浏览器都包含了知名的证书颁发机构的列表。因此,你也可以信任这个软件的供应商,提供并维护的信任列表。

在实际应用中,手动验证为每一个网站的证书(尽管你可以,如果你是这样的倾向)是不切实际。因此,最常见的解决方案是借助( 
图4-5 )证书颁发机构(CA)做这项工作 :在浏览器中指定哪些CA是可信任(根CA证书),CA负责验证你访问的每个网站,并进行审核,以确认这些证书没有被滥用或受损害。如果任何网站违反了CA的证书的安全性规定,那么CA有责任撤销其证书。

 

 

 

图4-5 CA数字证书签发

基本上每一个浏览器,都可以让你检查安全连接的信任链( 图4-6 ) -通常是通过点击URL旁边的锁形图标。

 

 

 

图4-6 igvita.com的证书信任链(谷歌浏览器,V25)

 

  • igvita.com 的证书、由 StartCom Class 1 Primary Intermediate Server签名
  • StartCom Class 1 Primary Intermediate Server 证书由 StartCom Certification Authority机构签发
  • StartCom Certification Authority是一个被认证的根证书签发机构。

信任链中的锚点就是证书签发机构的根证书,在上面的例子中就是是 StartCom Certification Authority。每一个浏览器会预先置入一个受信任的证书颁发机构(“根证书”)列表,在上面的例子中,浏览器信任和能够验证StartCom的根证书。因此,通过浏览器,浏览器供应商,StartCom证书颁发机构的信任传递链,我们扩展到信任我们访问的站。

 

每一个操作系统供应商和每一个浏览器都提供了一个默认信任的证书颁发机构列表。如果你很好奇,你可以通过搜索引擎搜索和查看这些清单。

在实践中,有数百个知名的和受信任的证书颁发机构,这也是系统的一个大麻烦。大量的CA导致了对您的浏览器的信任链大面积潜在的攻击。

 

 

证书吊销

偶尔证书的颁发机构可能需要撤销或作废证书,这可能由于证书的私钥被攻破了,证书颁发机构本身被攻破,或者其他一些正常的原因譬如证书替换、证书签发机构发生变化,等等。为了解决这个问题,证书本身包含了检查是否已吊销的逻辑(
图4-7 )。因此,为了确保信任链不会受到攻击影响,每个节点都可以检查每个证书的状态,连同签名。

 

 

 

图4-7 CRL和OCSP(谷歌浏览器,V25为igvita.com)

 

 

证书撤销列表(CRL)

证书撤销列表(CRL)是在RFC 5280中定义的,它制定了一个简单的机制来检查每个证书的状态:每个证书颁发机构维护并定期发布一份吊销证书序列号列表。任何人想验证一个证书,他可以从证书颁发机构下载吊销列表,并检查序列号是否在吊销列表中 - 如果找到,那么证书已经被撤销。

CRL文件本身可以定期公布,或在每次更新时都公布,CRL文件可以通过HTTP,或任何其他文件传输协议传输。该名单也是由CA签名,通常允许以指定的时间间隔缓存。在实际应用中,这个流程运行得很好,但也有一些场景CRL机制可能存在缺陷:

 

  • 越来越多的撤销意味着CRL列表只会越来越长,每个客户端必须获取整个序列号列表
  • 没有证书吊销即时通知机制 - 如果在客户端缓存期间,证书被吊销,客户端将认为证书是有效的,直到缓存过期。

 

 

在线证书状态协议(OCSP)

为了解决CRL机制的一些缺陷,在线证书状态协议(OCSP)在RFC 2560中被制定,它提供了一个实时执行证书状态检查的机制。不同于CRL,其包含了所有被撤销序列号,而OCSP只需要客户端传进序列号,就可以直接验证证书链。

因此,OCSP机制消耗更少的带宽,并能够提供实时验证。然而,没有任何机制是完美的!OCSP要求进行实时查询也引入了几个问题:

 

  • CA必须能够处理实时查询的实时性和负荷。
  • CA必须确保该服务在任何时候全球都可用。
  • 客户端在进行任何协商前都必须等待OCSP请求。
  • 因为CA知道哪些网站的客户端访问,实时的OCSP请求可能暴露客户的隐私。

 

在实际应用中,CRL和OCSP机制是互补的,大多数证书机构同时提供两种机制。

更重要的部分是客户端的支持和行为:一些浏览器发布自己的CRL列表,另外一些从CA中获取CRL文件并缓存它。同样,有些浏览器会进行实时的OCSP检查,但当OCSP请求失败时,浏览器的行为又各自有一些区别。如果你好奇,可以检查一下您的浏览器和操作系统的证书吊销设置!

 

 

TLS记录协议

TLS和下面的IP或TCP层比较起来没有什么本质不同,所有TLS会话内的数据交换采用了一个良好定义的协议( 
图4-8 )。TLS记录协议主要用来识别TLS中消息的类型(通过“Content Type”字段的数据来识别握手,Alert或数据),以及每个消息的完整性保护和验证。

 

 

 

图4-8 TLS记录协议结构

一个传输数据的典型流程如下:

 

  • 记录协议接收到应用数据
  • 数据被分成块:每个块最大2 14或16 KB字节
  • 应用程序数据可选的被压缩
  • 消息认证码(MAC)或HMAC
  • 使用协商的加密算法加密数据

一旦上述步骤完成后,加密的数据被向下传递到TCP层进行传输。在接收端,采用反向相同的工作流程:使用协商的加密算法对数据进行解密,验证MAC,提取的应用数据给应用层。

另一个好消息,所有上述的处理都是TLS层本身处理,对大多数应用程序是完全透明的。然而,TLS记录协议引入了几个你应该知道的影响:

 

  • TLS记录的最大大小为16KB
  • 每个记录包含一个5字节的头,MAC(SSLv3,TLS 1.0,TLS 1.1最多20个字节,TLS 1.2的多达32个字节),如果采用块加密算法则还有填充块(padding)
  • 为了解密和验证每一块数据,必须保证所有数据都已收到。

应用程序应该挑选合适的TLS记录大小,如果你可以去设置,这也可以是一个重要的优化。记录太小导致分帧过多,容易产生较大的开销,而记录过大,则在TCP层需要重新分块然后在接收端重新拼装,数据才可以被TLS层处理,并传递到您的应用层。

 

 

TLS优化

由于网络协议的分层架构,应用程序通过TLS和直接通过TCP运行对比,没有什么不同。因此,应用层基本上不用或者进行少量的修改,就可以通过TLS传输数据。也就是说,“TCP优化”的最佳实践完全可以用于TLS。

但是,你应该检查TLS部署:如何以及在哪里部署服务器,TLS记录的大小和内存缓冲区大小,证书大小,是否支持缩减握手,等等。服务器上这些参数配置如果正确,可以让用户体验产生较大的积极影响,也可以降低您的运营成本。

 

 

计算成本

建立和维护一个加密的通道,通信双方都引入了额外的一些计算成本。具体来说,首先是“TLS握手”中使用的不对称(公钥)加密算法 。然后,一旦建立了共享密钥的通信通道后,后续数据都采用对称密钥来加密。

正如我们前面提到的,公钥加密与对称密钥加密相比,需要消耗更多的CPU计算,web流行早期,往往需要额外的硬件来执行“SSL负载分担”。好消息是,这一切不再需要。现在硬件已经发生了翻天覆地的变化,这些CPU运算已经变得微不足道了,以前需要额外硬件才能完成的运算,现在完全可以在CPU上直接完成了。譬如Facebook和Google这些大型公司,正为数以百万计的用户提供TLS服务,所有必要的TLS协商和CPU运算都在普通的硬件中执行。

 

 

2010年1月,Gmail缺省情况下采用HTTPS提供一切服务。此前,HTTPS只是作为一个可选项,但现在我们所有的用户都使用HTTPS来保护他们的电子邮件安全。为了做到这一点,我们没有部署额外的机器,也没有部署特别的硬件。对我们产品前置机来说,SSL / TLS账户的CPU的负载小于1%,每个连接占用不到10 KB的的内存,不到2%的网络开销。许多人认为SSL / TLS需要花费大量的CPU运算时间,我们希望上面的数字(首次公开),将有助于消除大家的误解。

如果你现在停止往下阅读,你只需要记住一件事:SSL / TLS不不属于CPU消耗型操作。

 

 

- Adam Langley (Google)

 

 

 

我们已经使用硬件和软件负载平衡器大规模的部署了TLS服务。我们发现,目前基于软件的TLS实现,完全可以运行在普通的CPU上,其速度已经足够快的用来处理繁重的HTTPS流量负载了,无需借助专用加密硬件。我们所有HTTPS服务都是使用运行在普通硬件上的软件。

 

 

- Doug Beaver (Facebook)

 

尽管如此,“TLS会话恢复”等技术仍然是重要的优化,这将有助于降低计算成本和公共密钥加密TLS握手带来的延迟-你没有必要去无谓增加你的CPU负荷,在没有任何必要的前提下。

 

说起优化CPU负荷,我们还需要确保SSL库升级到最新的版本,并采用他们来建立你的Web服务器或代理服务器!例如,最近版本的OpenSSL取得了显着的性能改进,没准,你的系统默认的OpenSSL库早已过时。

 

 

提前终止

TLS连接的延迟,无论是新的连接或恢复连接,都是优化需要重点考虑的一个领域。首先,我们前面描述过,每一个TCP连接以“三次握手”开始 ,这需要一个完整的往返(SYN
/ SYN-ACK包)。接着, 是“TLS握手”,最多需要两个额外的往返(RTT),或一个往返,如果使用“TLS会话恢复” 的话。

在最坏的情况下,在任何应用程序交换数据之前,TCP和TLS连接过程需要三个完整的往返!以我们前面的例子作参考,客户端在在纽约,服务器在伦敦,一个完整的往返是56ms(
表1-1 ),这意味着一个完整的TCP和TLS建立至少有168ms的延迟,如果采用TLS会话恢复则至少有112ms的往返延迟。更糟的是,两端之间的传播延迟越高,TLS连接的延迟也是越大,56 ms绝对是一个很乐观的数字!

因为TLS会话运行在TCP之上。“TCP优化”,的所有优化建议同样也适用于TLS。如果TCP连接复用对于未加密传输来说是一个重要的考虑因素,那么它对通过TLS进行数据传输的应用来说,同样也是一个关键的优化
- 如果你能避免做握手,尽量避免。如果你确实要执行握手操作,那么你可能需要了解一下“提前终止”的技术。

正如我们在第1章中讨论的那样,我们不能期望减少数据包的传播延迟,毕竟他们已经是光速级了。然而,我们可能无法使我们的包传输更快,但我们可以让他们的传输更短的距离!提前终止是一个简单的技术,将您的服务器更贴近用户(
图4-9 ),使每个客户端和服务器之间的往返延迟成本降到最低。

 

 

 

图4-9 提前终止客户端连接

做到这一点最简单的方法是在世界各地部署服务器,并复制或缓存您的数据和服务,而不是让每用户漂洋过海连接到您的主服务器。当然,这也正是许多CDN提供的服务。然而,使用地理上分布的服务器不能阻止我们继续优化。

采用靠近客户端的服务器来代理建立TLS会话,这意味着TCP和TLS握手往返可以更快,延迟大大降低。反过来,这个服务器可以再与主服务器之间建立一个安全的长连接,并代理所有传入的请求和主服务器返回的响应数据。

简而言之,将服务器移到离客户更近的地方,加速TCP和TLS握手过程!大多数CDN提供商提供这项服务,如果你喜欢冒险,你也可以用最少的成本部署自己的基础设施:利用几个数据中心的提供的分布全球的云服务器,在这些云服务器上配置代理服务器用来转发到你服务器的请求,同时添加基于地理的DNS负载均衡,一切OK了。

 

 

未缓存源获取

CDN或代理服务器缓存资源所采用的技术方案,可能需要基于不同用户进行定制,或者由于一些请求中包含了私有数据导致无法缓存,因此不可能在CDN中缓存所有的资源,就是俗称的“未缓存源获取“。

虽然当数据缓存在基于地理分布的世界各地服务器中时,CDN的性能达到最优,未缓存起源获取仍然提供了一个非常重要的优化:客户端与附近的服务器之间建立TLS连接,这还是可以显着减少握手延迟。反过来,CDN或自己的代理服务器,可以保持一个“活连接池”来转发源服务器的数据,让你提供一个快速响应给客户端。

事实上,作为一种优化技术,一些CDN在通信的两端附近都提供了CDN服务器!客户端与附近的CDN节点建立TLS连接,然后这个CDN节点将请求中转到靠近源服务器的CDN节点,然后请求再被转发到原服务器。在CDN网络中增加的这额外一跳允许通信经由优化的CDN骨干网络路由,它可以进一步降低客户端和原始服务器之间的延迟。

 

 

会话缓存和无状态恢复

在靠近用户的地方建立连接,是一种重要的优化手段,这将为您的用户在所有的情况下降低延迟。但是,传输再少的数据也快不过什么都不传输。启用的TLS会话缓存和无状态恢复,可以让你在重复连接时消除一个完整的往返。

TLS会话缓存技术依赖的会话标识符,在SSL 2.0中被引入,目前已经被大多数的客户端和服务器支持。但是,如果您要配置您的SSL / TLS服务器,不要理所当然的认为这些在默认情况下都是支持的。事实上,在默认情况下,大多数服务器都是关闭的 - 你需要了解这一点!你应该仔细检查并验证您的配置:

 

  • 多进程或者多服务的服务器,应该使用一个共享的会话缓存。
  • 共享的会话缓存大小应该根据你的流量进行调整。
  • 会话超时机制必须被提供。
  • 在一个多服务的服务器中,来自同一IP的客户端,或者相同TLS会话ID的客户端路由到同一台服务器,这将提供更好的会话缓存利用率。
  • “基于IP地址或者cookie”负载均衡对会话共享不是一个好选择,应当在不同的服务器之间充分的使用共享缓存,以获得更好的缓存利用率。
  • 检查和监测SSL / TLS会话缓存性能统计数据以获得最佳性能。

另外,如果客户端和服务器都支持Session Ticket,则所有的会话数据将被存储在客户端上,上述步骤就没有必要了 - 一切都简单明了了。然而,因为Session Ticket是TLS中一个相对较新的扩展,并非所有的客户端都支持。在实际应用中,为了获得最佳性能,你最好同时启用两种方案,对支持Session Ticket的客户端采用Session Ticket方案,而对旧客户端采用会话标识作为备选方案。这些方案都不是排他性的,他们可以很好的一起工作。

 

 

TLS记录大小

所有通过TLS传输的应用数据在都是封装在记录协议中( 图4-8 )。每个记录的最大大小是16KB,并根据所选择的加密算法,每个记录将添加20-40字节的额外头域,MAC,和可选的填充数据。如果记录随后被装入到一个TCP数据包中,那么我们还需要添加IP头和TCP头:20字节的IP头,20字节的TCP头(不带任何选项)。其结果是,每一个记录都有60〜100字节的潜在开销。对于一个典型的以太网最大传输单元(MTU)大小1500字节,这样的数据包结构至少有6%的性能损耗。

记录越小,越高的帧开销。然而,简单地将记录大小设置为最大尺寸(16 KB)也未必是一个好主意!如果一条记录跨越多个TCP包,TLS层必须等待所有的TCP报文到达之后,它才能对数据进行解密(
图4-10 )。如果其中某些TCP报文丢失,重新排序,或由于拥塞控制而阻塞,那么TLS记录的其余片段都要放在缓冲区中,直到全部收到后才可以解码,从而导致额外的延迟。在实际应用中,这些延迟浏览器可以为浏览器带来了比较严重的性能瓶颈,浏览器一般倾向于收到一个字节处理一个字节。

 

 

 

图4-10 Wireshark抓包 11,211字节的TLS记录分成了8个TCP Segments

小记录产生更多冗余开销,大记录产生延迟,没有一个“完全准确”的记录大小。然而,对于Web应用程序来说,主要都是通过浏览器进行访问,最佳做法很简单:每个TCP数据包正好包含一个TLS记录,TLS记录应该尽量占据整个TCP最大段大小(MSS)。换句话说,不要使TLS记录跨越多个TCP数据包,并在TLS记录中填充尽可能多的数据。基于下面的原则来确定最佳TLS记录大小:

 

  • 分配20字节给IPv4帧,40字节给IPv6帧
  • 分配20字节给TCP帧
  • 分配40个字节(时间戳,SACK)给TCP选项

假设一个正常的1500字节MTU,这留给TLS记录的就是在IPv4下大概1420字节,IPv6下1400字节。考虑到未来的需求,最好使用IPv6的大小:如果您的MTU是1400字节,TLS记录还要少一些。

不幸的是,我们在应用层很难去控制TLS记录大小的配置。相反,这通常是TLS服务器的一个设置,设置可能是一个编译器的常量或者标志 - 如何更新此值的详细信息,请检查您的服务器文档。

 

如果您的服务器正在处理大量的TLS连接,那么最大限度地减少每个连接内存使用量也是一个重要的优化。默认情况下,流行的如OpenSSL库将为每个连接分配最多50 KB的内存,但根据TLS记录大小,你可以检查文档或源代码看看如何调整这个值,这是一个不错的内存优化点。谷歌的服务器将他们的OpenSSL的缓冲区减少到了约5 KB。

 

 

TLS压缩

一个鲜为人知的TLS特性就是在其纪录协议中已经内置支持无损数据压缩:压缩算法在TLS握手过程中协商,数据在加密应用之前进行压缩。然而,在实际应用中,你应该在你的服务器上禁用TLS压缩,有以下几个原因:

 

  • “CRIME”的攻击,在2012年被公布,利用TLS压缩来恢复身份验证的Cookie,并允许攻击者进行会话劫持。
  • TLS压缩属于传输层压缩,他无法感知数据内容,他将去重复压缩已压缩的数据(比如图片,视频等)。

重复压缩会同时在服务器和客户端浪费CPU时间,另外,安全漏洞的影响也是相当的严重 - 因此需要禁用压缩TLS。在实际应用中,大多数的浏览器都是禁用TLS压缩的,不过,你应该明确的在您的服务器上明确的禁用,以保护您的用户。

 

相反,我们不应该依赖于TLS的压缩,而是应该在应用层配置Gzip,使所有文本型资源采用gzip方式传输,而对其他格式如图像,视频和音频应该去选取最佳的压缩格式。

 

 

证书链的长度

验证信任链要求浏览器遍历证书链,从网站证书,并递归地验证证书的父证书,直到达到一个受信任的根证书。因此,首先你应该做的优化就是验证服务器是否包含了执行握手时的所有中间证书。如果服务器缺失了中间的某个证书,许多浏览器将仍然工作,但他们将被迫暂停下来,自行去获取中间证书、验证它,然后继续 - 这可能需要新的DNS查找,TCP连接,一个HTTP GET请求,又增加了数百ms的延迟。

 

浏览器如何知道从哪里获取中间证书?通常子证书中会包含父证书的地址。

相反,确保你的证书链中不要包含不必要的证书!或者,更普遍的是,你应该最大限度地减少您的证书链的大小。回想一下,服务器证书在“TLS握手”期间发送 ,这时有可能处于在一个新的TCP连接中的前期阶段,它正在应用“慢启动”算法。如果证书链超过TCP的初始拥塞窗口(
图4-11 ),我们会在不经意间添加了又一往返握手:证书长度超出拥塞窗口大小,并导致服务器停止并等待客户端的ACK,然后再继续。

 

 

 

图4-11 Wireshark抓包 一个5323 byte的TLS证书链

图4-11的证书链是超过5 KB,将超过很多旧服务器的初始拥塞窗口大小,并带来另一个往返延迟。一个可能的解决方案是增加初始拥塞窗口大小-请参阅“增加TCP的初始拥塞窗口” 。此外,你应该检查一下是否可能减少证书的大小:

 

    • 尽量减少中间CA的数量。理想情况下,您所发送的证书链应该只包含两个证书:您的网站证书,以及CA的中间证书 - 选择您的CA时这个应该作为一个标准。第三个证书,这是CA根证书,应该已经是在浏览器中受信任的