Web 性能优化:TLS
- Web 性能优化:TCP
- 🎯 Web 性能优化:TLS
- Web 性能优化:HTTP
"do it, do it work, do it better ... and secure ☠️"
随着追逐利益而来的恶意参与者越来越多,当前的 Web 应用,已经从野蛮生长转而越来越多地关注安全 —— 通信安全、数据安全、用户安全、企业安全,甚至国家安全。作为通过网络传输数据的应用,最简单的获取安全的方式是采用 TLS。
TLS(Transport Layer Security),即传输层安全,TLS 直接构建在传输层之上,一般基于 TCP,也可以基于 UDP(如 DTLS)。TLS 负责将待传输的数据加密,加密后传输过程中任何中间方都再看不到具体内容,保障了安全性。关于 TLS 的更多内容可参见另一篇博文 简单解释TLS,本篇不再赘述。
我们的关注点重新集中到性能上,任何的引入必然导致额外的开销,对于 Web 应用来说,最要命的开销就是延迟。那么可以从哪些方面着手优化延迟?
会话复用
在上一篇关于 TCP 的讨论中,我们知道每次 tcp 连接的建立都需要经过三次握手,产生至少一个 RTT(Round-Trip Time) 的延迟,现在引入 tls,情况又如何?
对于建立在 tcp 之上的 tls,除了因为 tcp 三次握手引入的一个 RTT 延迟外,根据 tls 协议设计,TLS 会话的一次完整握手还会再引入 2 个 RTT 的延迟,所以一个 https 连接的初次建立过程会引入 3 个 RTT 延迟。
tcp 通过连接复用实现在需要的时候规避握手延迟,类似地,tls 也有会话复用机制——称为简短握手,可将 2 次 RTT 延迟减少到 1 次。
这是如何做到的呢?在建立会话时,服务器会创建一个 32 byte 的会话 id —— 会话 id 可以关联到该次会话协商出的一系列加密参数,然后通过 ServerHello 消息发送给客户端。在下一次通信时,客户端可以在发送的 ClientHello 消息中包含上一次的会话 id,如果服务器认可,则接下来就可以基于上一次会话的会话参数开始通信,从而省去 1 次 RTT。
在需要同时发起多个 https 连接时,可以重用第一个 tls 握手协商出来的会话参数。
一般服务器端可以将 tls 会话的有效时间设置为 1 天,对于高并发场景,可能导致会话缓存占用过多的内存资源,必须要设计好相应的缓存淘汰机制,或者采用会话记录单机制 —— 服务端使用密钥将会话相关的参数加密后发给客户端,由客户端保存,后续需要时客户端将加密信息发回服务端,服务端解密后使用。
TLS 记录
TLS 会将消息拆分为若干条 TLS 记录,每条记录上限为 16 KB。TLS 记录太小的话,在 tcp 分组中占比太小,浪费 tcp 分组;TLS 记录太大的话,会分拆为若干 tcp 分组,此时需要等待所有分组到达,才能解密出数据,引入额外延迟。
From optimizing-tls-record-size-and-buffering-latency
所以最好将 TLS 记录大小配置为恰好放入一个 tcp 分组,也可以配置为根据相关窗口大小动态调整记录大小。
证书验证
TLS 的安全是建立在 PKI 体系之上,在 PKI 体系中,由权威证书颁发机构给网站颁发身份证书,在 tls 握手过程中,服务器需要发送证书给客户端以验明正身。但证书这玩意,有生就有死,所以客户端在收到证书后,除了查看证书里签发时写入的静态信息是否确实可信,还要验证证书在当前时刻是否还有效。
这是一个典型的响应优先还是一致性优先的问题,方案不外乎两种:缓存 or 实时。
- 缓存方案:证书颁发机构会维护一个已撤销证书列表,称为 CRL(Certificate Revocation List,证书撤销名单),服务器或者客户端可以缓存到本地使用。缓存的问题就是,有可能你刚刚缓存过一次,就有一个证书被撤销了,导致缓存与实际状态不一致。
- 实时方案:鉴于缓存方案的问题,提供了互补的实时查询方案,称为 OCSP(在线证书状态协议),OCSP 的问题在于可能会阻塞 tls 协商过程,需要等待 OCSP 验证完成才能继续,特别是内网环境使用 IE + 自签名证书开发时容易被坑,博主就被坑过一次。如果访问不了证书颁发机构,且环境允许,可以考虑禁用 OCSP。
出于安全考虑,一般网站的证书不会直接使用根证书签发,而是使用(根证书签发出来的)中间证书签发,而根证书密钥则被离线存放。由于浏览器验证证书时,会顺着证书链验证每个节点的证书,所以这里有几个可以优化的点:
- 服务端在发送时最好同时把中间证书也一并附上,避免浏览器再单独发起一次中间证书请求,引入额外的延迟;
- 发送时无需包含根证书,因为已经存在客户端了;
- 签发证书层次不要过多,最好就一层中间证书,否则发送证书链时,数据太多;
- 非必要的证书就不要发了,避免传输数据过多,导致额外的分片,引入额外延迟;
- OCSP stapling,由服务端来发起 OCSP 请求,并将请求响应放在给客户端的证书链中,需要浏览器支持。
- 使用椭圆曲线证书链,比 RSA 证书链小,传输数据少一点;
- 同一张证书不要绑定过多域名,传输数据少一点。
其他
ALPN(Application Layer Protocol Negotiation)
在 tls 握手过程中嵌入 ALPN 机制,协商上层使用的应用协议,不用等待 tls 建立后单独协商,减少延迟,整个过程就是在 ClientHello 中增加一个 ProtocolNameList 字段,服务器收到后在 ServerHello 回复中返回选中的协议。
参考
- 《Web 性能权威指南》
- 《HTTPS 权威指南》