HTTPS优化:流程分析和优化的措施

/ 转载 / 没有评论 / 329浏览

转载跳转~


HTTPS请求花费的时间?


一个全新的HTTPS链接,从发起请求到数据返回经过几个RTT?


假设没有任何缓存,一个HTTPS请求得经过10个RTT才能返回内容。


一个RTT如果是50ms,这个全新的请求至少花费 50*10=500ms。这还没有算后端业务处理的时间。HTTPS请求延迟确实比较




通常情况下业务HTTP的延迟容忍度较差,Server To Client的模式,效率总归是越高越好。


现实情况会有各种缓存,少不必要的消耗,所以很少发生上面这种极端的10个RTT情况。



在有各类缓存情况下,一个常规的HTTPS请求会保留下面4个RTT流程。



除了HTTPS内容请求外,握手的阶段或者TLS层是否还可以再继续优化一下,让我们的APP或者视频播放再快那么一点?


答案:肯定是的!


之前一篇文章介绍过HTTPS:通过非对称加密交换对称加密的参数,从而实现内容交换的安全性。


了解这一点,对于HTTP这种协议,我们可以从这几方面入手:提升加解密效率、减少内容传输量、链接复用...




HTTPS流程分析和优化的策略


WireShark抓包,看我们线上某个接口服务HTTPS包。


TLS1.2协议 ECDHE算法



根据上面的HTTPS包,我们画了下面流程图,一步步分析流程,并确定中间可优化的点。





整个流程有以下几个阶段:




TLS Hello阶段的分析与应用。


先看看Client hello的几个主要的参数

strct hello {  Version // TLS版本号  Random  // 客户端随机数  Session id   Cipher Suites // 客户端支持的加密套件  Extension: support version; // 扩展TLS版本支持}


上面的抓包 Version是 TLS1.2,扩展中带有TLS1.3。如果服务端支持1.3 ,将改为1.3协议。


服务端先要进行对应的支持配置,如下。

ssl_protocols  TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;


Cipher Suites是个数组,会包含新旧一堆加密算法支持,优先级从上到下。


服务端Server Hello返回 Cipher Suite,根据Client的请求匹配一个合适的加密套件。如下服务器返回支持的套件。

Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA256


上面什么意思?


握手时对称加密参数交换使用RSA。通信加密使用256位长度对称AES算法, GCM和SHA256是AES的分组模式和摘要算法。


这个阶段优化的几个思路:


密钥交换算法改进:RSA 可以改为DH类算法(Diffie-Hellman),如ECDHE。

在同等复杂度下,计算效率更高,证书体积也更小。


优化ECDHE算法实现,优先选择效率最高的椭圆曲线实现x25519。


在Nginx后台 使用 ssl_ecdh_curve 配置椭圆曲线优先级。

ssl_ecdh_curve X25519:secp384r1;


同时ECDHE支持  False Start。


启用了False Start,在Client发送完ChangeCipher Spec就可以发送加密的应用数据,减少1个RTT等待时间。


服务端配置Cipher Suites的优先级。把性能最高的算法放在前面。

ssl_prefer_server_ciphers on; // 开启False Start ssl_ciphers EECDH+ECDSA+AES128+SHA:RSA+AES128+SHA...


尽管AES的效率很高,但在一些安全性没那么高的业务下,AES密钥的长度可以小一些,256位改为128位。



Server Certificate阶段


Certificate阶段会将自身公钥和证书CA中间公钥发给客户端。

本地验证证书合法后继续 Client Key Exchange流程。


在这个阶段,有两个方向可以优化:证书传输+证书验证


证书传输

证书的大小当然越小越好,

在生成证书阶段选择ECDSA,而不是RSA证书,安全不变的条件下,密钥长度更小,运算量也更小。


比如用类似下面生成ECDSA证书

openssl ecparam -genkey -name prime256v1 -out key.pem


证书验证


客户端在验证证书时,会走证书链逐级验证,而且为了知道证书是否被CA吊销,客户端会访问CA下载OCSP数据,确认证书是否有效。


OCSP 需要向 CA 查询,因此也是要发生网络请求,如果CA服务器的延迟过大,会导致客户端在校验证书这一环节的延时变大。


OCSP Stapling

为了解决证书有效性验证的问题,出现了OCSP Stapling。


服务器向 CA 周期性地查询证书状态,获得一个带有时间戳和签名的响应并缓存,当客户端来请求证书,在TLS握手阶段,服务器将该结果给客户端。


因为结果带有 CA 私钥的签名,所有结果可信,客户端在本地就可以判断证书的有效性。



在Nginx中开启OCSP Stapling

ssl_stapling               on;ssl_stapling_verify        on;ssl_trusted_certificate    xx.pem


使用以下命令测试服务器是否开启 ocsp staping

openssl s_client -connect ip:443 -statusOCSP responseno response sent //出现以下 则没配置



密钥交换和验证的阶段:



Client/ServerKey Exchange 阶段


在这一阶段主要是交换DH加密公钥,如选定椭圆曲线,生成椭圆曲线公钥,公钥签名,与客户端的Client  Key Exchange呼应,最终获取对方的公钥,用来加密AES的密钥。


服务器最后一次:Hello Done。 表面服务器已经传输完所有的加密信息了。


加解密双方验证

Change Cipher Spec/Encrypted Handshake Message


在Key Change阶段,加密的参数均已生成,双方有了交换密钥的公钥,也有对称密钥的参数。可以进行数据传输了


如果双方都验证加密和解密没问题,那么握手正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。


以上两个阶段是否可以优化?


是的,我们可以采用链接复用的方式跳过该阶段


链接复用优化:


复用有两个方式:SessionID 和 SessionTicket,实现不同但目标一致。


SessionID:


客户端和服务器首次 TLS 握手连接后,双方会在内存缓存会话密钥,并用唯一的 Session ID 来标识。


在下一次链接时,服务端可以知道一个进来的连接是否在之前已经建立过连接,如果在服务器中也存在这个 session 的 key,那么它就能重用。


在服务端开启SessionID

ssl_session_cache shared:SSL:50m;ssl_session_timeout 1d;


SessionTicket


为了解决 Session ID 的问题,就出现了 Session Ticket,服务器不再缓存每个客户端的会话密钥,而是把缓存的工作交给了客户端,类似于 HTTP 的 Cookie。


客户端与服务器首次建立连接时,服务器会加密「会话密钥」作为 Ticket 发给客户端,交给客户端缓存该 Ticket。


客户端再次连接服务器时,客户端会发送 Ticket,服务器解密后就可以获取上一次的会话密钥,然后验证有效期,如果没问题,就可以恢复会话了。


在服务端开启Session ticket

ssl_session_tickets on;ssl_session_ticket_key xx.key;


用了链接重用技术,当再次重连 HTTPS 时,只需要 1 RTT 就可以恢复会话。对于 TLS1.3 使用 Pre-shared Key 重用技术,可以实现0RTT恢复链接。


采用链接重用,当然不可避免的产生重放攻击风险。


对于安全性要求非常高的业务要慎重的使用链接重用, 当然如果调整重用的过期时间,同时业务端做好安全开发,收益也非常大。