基础-https-优化


HTTPS如何优化

分析性能损耗

  • TLS协议握手过程
    • TLS握手过程增加了网络延迟(2RTT)
    • 对于ECDHE密钥协商算法,握手过程中客户端和服务端都需要临时生成椭圆曲线公钥
    • 客户端在验证证书是,会访问CA获取CRL或OCSP,目的是验证服务器证书是否有被吊销
    • 双方计算Pre-Master,也就是对称加密密钥
  • 握手后的对称加密报文传输
    • 现在主流的对称加密算法AES、ChaCha20,而且一些CPU厂商还针对他们做了硬件级别的优化,因此这个环节的性能消耗非常小
img

硬件优化

  • 选择支持AES-NI特性的CPU
  • 如果CPU不支持,可以选择ChaCha20对称加密算法,运算指令对CPU更友好一点

软件优化

  • 软件升级:Linux内核升级、OpenSSL升级

协议优化

密钥交换算法优化

TLS1.2版版如果使用RSA算法,那么需要4次握手,也就是要花费2RTT,才可以进行应用数据的传输,而且RSA算法不具备前向安全性。不仅慢,而且安全性不高。

如果可以使用ECDHE算法,由于支持False Start(抢跑),客户端可以摘TLS协议的第三次握手有,第四次握手前,发送加密的应用数据,以此将TLS握手的消息往返由2RTT减少到1RTT,而且安全性也高,具备前向安全性。

ECDHE算法是基于椭圆曲线实现的,不同椭圆曲线性能也不同,应该尽量选择x25519曲线,该曲线是目前最快的椭圆曲线。比如在Nginx上,可以使用ssl_ecdh_curve指令配置想使用的椭圆曲线,把有限使用的放在前面。

ssl_ecdh_curve X25519:secp384r1;

对于对称加密算法方面,如果安全性不是特别高的要求,可以选用AES_128_GCM,它比AES_256_GCM快一些,因为秘钥长度短。比如在Nginx上可以使用ssl_ciphers指令配置想使用的非对称加密和对称加密算法,也就是密码套件,而且吧性能最快最安全的放在最前面。

ssl_ciphers 'EECDH+ECDSA+AES128+SHA:RSA+AES128+SHA'

TLS升级

如果可以,直接把TLS1.2升级成TLS1.3。TLS1.3大幅简化了握手的步骤,完成TLS握手只要1RTT,而且安全性更高。

TLS1.2的握手中,一般需要4次握手,先要通过Client Hello和Server Hello消息协商出后续使用的加密算法,再互相交换公钥,然后计算出最终的会话秘钥

  • img

TLS1.3的握手过程,把Hello和公钥交换着两个消息合并成了一个消息,于是这样就减少到只需要1RTT就能完成TLS握手。怎么合并的和,具体做饭是在客户端Client Hello消息里带上直尺的椭圆曲线,依旧椭圆曲线对应的公钥;服务端收到后,选定一个椭圆曲线,然后返回消息时,带上服务端的公钥。经过这1个RTT双方都已经有生成会话秘钥的材料了,于是客户端算出会话秘钥,就可以进行应用数据的加密传输了。

而且,TLS1.2对密码套件进行了减肥,对于秘钥交换算法,废除了不支持前向安全性的RSA和DH算法只支持ECDHE算法。对于对称加密和签名算法,只支持目前最安全的几个密码套件。

  • TLS_AES_256_GCM_SHA384
  • TLS_CHACHA20_POLY1305_SHA256
  • TLS_AES_128_GCM_SHA256
  • TLS_AES_128_CCM_8_SHA256
  • TLS_AES_128_CCM_SHA256

之所以仅仅支持这么骚的密码套件,是因为TLS1.2由于支持各种古老且不安全的密码套件,中间人可以利用降级攻击,伪造客户端的Client Hello消息,替换客户端支持的密码套件位一些安全的密码套件,使得服务器被迫使用这些密码套件进行HTTPS连接,从而破解密文

证书优化

证书传输优化

要让证书更便于传输,那必然是减少证书的大小,这样可以节约带宽,也能让客户端减少运算量。所以对于服务器的证书应该选择椭圆曲线(ECDHE)证书,而部署RSA证书,因为在相同的安全强度下,ECC秘钥长度比RSA短的多

证书验证优化

服务器向 CA 周期性地查询证书状态,获得一个带有时间戳和签名的响应结果并缓存它。服务器向 CA 周期性地查询证书状态,获得一个带有时间戳和签名的响应结果并缓存它。

会话复用

TLS我收的目的就是为了协商出会话密钥,也就是对称加密密钥,那么我们如果把首次TLS握手协商的对称加密密钥缓存起来,待下次需要建立HTTPS连接时,直接复用这个秘钥。

  • Session ID

    • 客户端和服务端首次TLS握手连接后,双方会在内存缓存会话秘钥,并用唯一的sessionID来表示,sessionID和会话秘钥相当于key-value的关系
    • 当客户端再次连接时,hello消息里会带上sessionID,服务器收到后就会从内存找,如果找到就直接用该会话秘钥恢复会话状态,跳过其余过程,只用一个消息往返就可以建立安全通信。当然为了安全性,内存中的会话秘钥会定期失效。
    • 缺点
      • 服务端必须保持每一个客户端的会话秘钥,随着客户端增多,服务器的内存压力也越大
      • 现在网站服务一般是由多台服务器通过负载均衡提供服务的,客户端再次连接不一定会命中上一次访问过的服务器,于是还要走完整的TLS握手过程
  • Session Ticket

    • 为了解决SessionID的问题,就出现了session ticker,服务器不在缓存每个客户端的会话秘钥,而是吧缓存的工作交给客户端,类似于HTTP的Cookie
    • 客户端与服务器首次建立连接时,服务端会加密会话秘钥,作为Ticket发给客户端,交给客户端缓存该Ticket。客户端再次连接服务器时,客户端会发送Ticket,服务器解密后就可以获取上一次的会话秘钥,然后验证有效期,如果没问题,就可以恢复会话了,开始加密通信。
    • 对于集群服务器的话,要确保每台服务器加密会话秘钥的秘钥是一致的,这样客户端携带Ticket访问任意一台服务器时,都能恢复会话。
    • SessionID和SessionTicket都不具备前向安全性,因为一旦加密的密钥被破解或者服务器泄露秘钥,前面劫持的通信密文都会被破解。同时应对重放攻击也很困难,重放攻击的危险在于,如果中间人截获了某个客户端的SessionID或SessionTicket以及Post报文,而一般Post请求会改变数据库的数据,中间人就利用此截获的报文,不断向服务器发送该报文,这样就会导致数据库的数据被中间人改变,而用户是不知情的。避免重放攻击的方式就是需要对绘画秘钥设定一个合理的过期时间,以及只针对安全的HTTP请求如Get和Head使用会话重用。

    Pre-shared Key

    前面的SessionID和SessionTicket方式都需要在1RTT才能恢复会话。

    而TLS1.3更为厉害,对于重连TLS1.3只需要0RTT,原理和Ticket类似,只不过在重连是,客户端会把Ticket和HTTP请求异同发送给服务端,这种方式叫Pre-shared Key。Pre-shared Key也有重放攻击的危险。

    img


  目录