当前位置: 首页 > 科技观察

字节两侧:优化HTTPS的方法你知道多少?_0

时间:2023-03-14 17:27:47 科技观察

将原始数据传输的HTTP协议转换为加密数据传输的HTTPS协议,为应用数据设置了“保护伞”,提高了安全性的同时也带来了性能消耗。因为HTTPS比HTTP协议多了一次TLS协议握手过程,目的是通过非对称加密握手来协商或交换对称加密密钥,这个过程最多可以花费2个RTT,之后应用数据的后续传输必须使用对称加密密钥加密/解密。为了数据安全,我们不得不使用HTTPS协议。到目前为止,大部分的URL都已经从HTTP协议迁移到HTTPS协议,所以针对HTTPS的优化非常重要。这一次,我们将从多个角度优化HTTPS。分析性能损失既然要优化HTTPS,那么就要知道哪些步骤会造成性能损失,然后对症下药。产生性能消耗的环节有两个:第一个环节,TLS协议握手过程;第二个环节,握手后的对称加密报文传输。对于第二阶段,目前主流的对称加密算法AES和ChaCha20都有不错的性能,而且有CPU厂商在硬件层面进行了优化,所以这一阶段的性能消耗可以说是非常小的。在第一个链路中,TLS协议的握手过程不仅增加了网络延迟(最多可以花费2个RTT),而且握手过程中的一些步骤也会造成性能损失,例如:对于ECDHE密钥协议算法,握手过程客户端和服务端都需要临时生成椭圆曲线公私钥;客户端验证证书时,会访问CA获取CRL或OCSP来验证服务器的证书是否被吊销;双方计算Pre-Master,即会话密钥;为了更清楚这些步骤在TLS协议握手的哪个阶段,我画了这张图:硬件优化玩游戏的时候,如果不能打败对方,那么有最有效最快的办法成为更强,那就是“充钱”。如果还是不行,说明你还没有充够电。计算机也是如此。软件在物理硬件上运行。硬件越强大,软件运行的速度就越快。因此,如果要优化HTTPS优化,最直接的方法就是花钱购买性能参数更好的硬件。但是你必须把钱花在正确的方向上。HTTPS协议是计算密集型的,不是I/O密集型的,所以不能把钱花在网卡、硬盘等上,而是花在CPU上。一个好的CPU可以提高计算性能,因为在HTTPS连接过程中有很多进程需要计算密钥,所以这样可以加快TLS握手过程。另外,如果可能的话,应该选择支持AES-NI特性的CPU,因为这类CPU可以在指令层面优化AES算法,从而加快数据加解密传输过程。如果你的服务器是Linux系统,可以使用如下命令查看CPU是否支持AES-NI指令集:如果我们的CPU支持AES-NI特性,那么对称加密算法应该选择AES算法。否则,可以选择ChaCha20对称加密算法,因为ChaCha20算法的运算指令比AES算法对CPU更友好。软件优化如果公司预算充足,可以考虑为新服务器购买更好的CPU,但是对于已经在使用的服务器,硬件优化可能并不合适,所以需要从软件的方向进行优化。软件优化的方向可以分为两层,一是软件升级,二是协议优化。先说第一个软件升级。软件升级就是将正在使用的软件升级到最新版本,因为最新版本不仅提供了最新的功能,还优化了之前软件存在的问题或性能。例如:将Linux内核从2.x升级到4.x;和硬件升级一样也是棘手的问题,因为实施软件升级需要时间和人力,同时也存在一定的风险,也可能影响正常的在线服务。既然如此,我们就专注于协议优化,即通过对现有链路的微小改动来进行优化。协议优化协议的优化就是优化“密钥交换过程”。密钥交换算法优化如果TLS1.2版本使用RSA密钥交换算法,需要4次握手,即传输应用数据需要2个RTT,RSA密钥交换算法不具有前向安全性。简而言之,使用RSA密钥交换算法的TLS握手过程不仅速度慢,而且不太安全。因此,如果可能的话,尽量使用ECDHE密钥交换算法来代替RSA算法,因为这种算法支持“FalseStart”,也就是“抢占式运行”。在握手之前发送加密的应用数据,从而将TLS握手消息往返从2个RTT减少到1个RTT,安全性也很高,具有前向安全性。ECDHE算法是基于椭圆曲线实现的,不同的椭圆曲线有不同的性能。你应该尽量选择x25519曲线,这是目前最快的椭圆曲线。比如在Nginx上,你可以使用ssl_ecdh_curve命令配置你要使用的椭圆曲线,把首选的放在前面:对于对称加密算法,如果你对安全性没有特别高的要求,可以选择AES_128_GCM,这比AES_256_GCM更快。因为密钥的长度更短。比如在Nginx上,可以使用ssl_ciphers命令配置自己要使用的非对称加密算法和对称加密算法,也就是密钥套件,把最快最安全的算法放在最前面:TLSupgradeOfcourse,如果可能,直接将TLS1.2升级到TLS1.3。TLS1.3大大简化了握手步骤。完成TLS握手只需要1个RTT,安全性更高。在TLS1.2握手中,一般需要4次握手。首先,通过ClientHello(第一次握手)和ServerHello(第二次握手)消息协商要使用的加密算法,然后相互交换公钥(第二次握手)。3和4次握手),然后计算最终的会话密钥。下图左边是TLS1.2的握手过程:上图右边是TLS1.3的握手过程。可以发现,TLS1.3将Hello和publickeyExchange这两个消息合并为一个消息,所以这减少到只有1个RTT来完成TLS握手。它是如何合并的?具体的,客户端在ClientHello消息中包含支持的椭圆曲线和这些椭圆曲线对应的公钥。服务器收到后,选择一个参数,比如椭圆曲线,然后用服务器的公钥返回消息。在这1个RTT之后,双方已经有了生成会话密钥的材料,客户端就可以计算出会话密钥,然后就可以进行应用数据的加密传输了。此外,TLS1.3“减肥”了密码套件。对于密钥交换算法,取消了不支持前向安全的RSA和DH算法,只支持ECDHE算法。对于对称加密和签名算法,目前仅支持最安全的密码套件。例如openssl只支持以下5个密码套件:TLS_AES_256_GCM_SHA384TLS_CHACHA20_POLY1305_SHA256TLS_AES_128_GCM_SHA256TLS_AES_128_CCM_8_SHA256TLS_AES_128_CCM_SHA25isonlysupportedby1suitecipherssuiteT..2由于支持各种古老且不安全的密码套件,中间人可以利用降级攻击伪造客户端的ClientHello消息,将客户端支持的密码套件替换为一些不安全的密码套件,强制服务器使用这个密码套件建立HTTPS连接,从而解密密文。证书优化为了验证服务器的身份,服务器会在TSL握手过程中将自己的证书发送给客户端,以证明其身份是可信的。对于证书的优化,有两个方向:一是证书传输,二是证书验证;如果证书传输优化使证书更容易传输,则必须减小证书的大小,这样可以节省带宽并减少客户端操作量。因此,对于服务器证书,应该选择椭圆曲线(ECDSA)证书而不是RSA证书,因为在同等安全强度下,ECC密钥长度比RSA短很多。证书验证优化客户端验证证书时,是一个复杂的过程。它将通过证书链逐步验证。验证过程不仅需要“用CA公钥解密证书”和“用签名算法验证证书的完整性”,而且为了知道证书是否被CA吊销,客户端有时会再次访问CA下载CRL或OCSP数据以确认证书的有效性。这个访问过程是HTTP访问,所以会产生一系列的网络通信开销,如DNS查询、连接建立、发送和接收数据等。CRLCRL称为证书撤销列表(CertificateRevocationList)。该列表由CA定期更新。列表的内容是被撤销信任的证书的序号。证书有效。但是CRL有两个问题:第一个问题是,由于CRL列表由CA维护并定期更新,如果证书刚刚被吊销,客户端在更新CRL之前仍然会信任该证书,而真正的-时间表现不佳;第二个问题是,随着吊销证书数量的增加,列表会越来越大,下载速度也会变慢。下载后,客户端要遍历这么大的列表,会导致客户端去验证证书。该链接有很大的延迟,这反过来又减慢了HTTPS连接。OCSP因此,现在基本上都是使用OCSP,称为在线证书状态协议(OnlineCertificateStatusProtocol)来查询证书的有效性。它通过向CA发送查询请求并让CA返回证书的有效状态来工作。客户端不需要像CRL方式那样下载一个大列表,还要从列表中查询。同时,由于可以实时查询每个证书的有效性,解决了CRL的实时性问题。OCSP需要查询CA,所以也需要进行网络请求,还要看CA服务器的“面子”。如果网络状况不佳,或者CA服务器繁忙,也会导致客户端延迟验证证书。变得更大。OCSPStapling于是为了解决这个网络开销,出现了OCSPStapling。其原理是:服务器周期性地向CA查询证书状态,得到带有时间戳和签名的响应结果并缓存起来。当客户端发起连接请求时,服务器会在TLS握手过程中将这个“响应结果”发送给客户端。由于签名的存在,服务端无法被篡改,所以客户端可以知道证书是否已经被吊销,不需要客户端再次查询。会话复用TLS握手的目的是协商会话密钥,即对称加密密钥。如果我们把第一次TLS握手协商好的对称加密密钥缓存起来,下次需要建立HTTPS连接时,可以直接“重用密钥”,这样不是减少了TLS握手的性能损失吗?这种方式就是sessionresumption(TLSsessionresumption)。会话恢复有两种:第一种称为SessionID;第二种称为SessionTicket;最后双方将会话密钥缓存在内存中,并用唯一的SessionID标识。SessionID和sessionkey相当于键值关系,当client再次连接时,sessionID会包含在hello消息中,server收到后会从内存中查找,如果找到了,会直接使用sessionkey恢复session状态,跳过剩下的过程,只用一条消息来回就可以建立安全通信,当然为了安全,sessionkey在内存中是周期性的盟友过期了。但它有两个缺点:服务器必须保存每个客户端的会话密钥,并且随着客户端数量的增加,服务器的内存压力也会增加。现在的网站服务一般都是由多台服务器通过负载均衡来提供的。客户端再次连接时,不一定会命中上次访问的服务器,因此需要经过一个完整的TLS握手过程;为了解决SessionID的问题,SessionTicketSessionTicket出现了。服务端不再缓存每个客户端的sessionkey,而是将缓存工作交给客户端,类似于HTTPcookies。当客户端第一次与服务器建立连接时,服务器会将“会话密钥”加密后作为Ticket发送给客户端,由客户端缓存。当客户端再次连接到服务器时,客户端会发送一个Ticket。服务器解密后,可以得到最后的会话密钥,然后验证有效期。如果没有问题,可以恢复会话并开始加密通信。对于集群服务器,需要保证每台服务器都用相同的密钥对“会话密钥”进行加密,这样当客户端使用Ticket访问任何一台服务器时,会话都可以恢复。SessionID和SessionTicket都没有前向安全性,因为一旦加密“会话密钥”的密钥被破解或者服务器泄露了“会话密钥”,之前被劫持的通信密文就会被破解。同时,应对重放攻击也非常困难。这里简单介绍一下重放攻击的工作原理。假设爱丽丝想向鲍勃证明她的身份。Bob要求Alice的密码作为身份证明,Alice应该尽最大努力提供该密码(可能在哈希函数等转换之后)。与此同时,夏娃偷听了谈话并保留了密码(或哈希)。交换后,Eve(假装是Alice)连接到Bob。当被要求提供身份证明时,Eve发送从Bob接受的最后一次会话中读取的Alice密码(或哈希),从而授予Eve访问权限。重放攻击的危险在于,如果中间人截获了客户端的SessionID或SessionTicket和POST消息,而一般的POST请求将更改数据库中的数据,中间人可以利用截获的消息不断向服务端发送消息,这会导致数据库中的数据被中间人更改,而客户端却毫无察觉。避免重放攻击的方法是为会话密钥设置一个合理的过期时间。Pre-sharedKey前面的SessionID和SessionTicket方法都需要1个RTT来恢复会话。而TLS1.3更强大。重新连接TLS1.3只需要0RTT。原理和Ticket类似,只是在重连时,客户端会将Ticket和HTTP请求一起发送给服务端。这种方法称为预共享密钥。同样,Pre-sharedKey也存在重放攻击的危险。如上图所示,假设中介通过某种方式使用会话重用技术拦截了客户端的POST请求。通常,POST请求会改变数据库中的数据,然后中介可以将截获的消息发送给服务器。服务器收到后,也认为是合法的,于是恢复了session,导致数据库中的数据又发生了变化,但此时用户并不知道。因此,应对重放攻击,可以为会话密钥设置合理的过期时间,只对GET/HEAD等安全的HTTP请求使用会话重用。总结一下硬件优化的方向,因为HTTPS是计算密集型的,应该选择计算能力更强的CPU,最好选择支持AES-NI特性的CPU。该特性可以在硬件层面优化AES对称加密算法,加速应用。数据加密和解密。对于软件优化的方向,如果有可能,将软件升级到更新的版本,比如将Linux内核2.X升级到4.X,将openssl1.0.1升级到1.1.1,因为新版本的软件不仅会提供新功能,还会修复旧版本的问题。对于协议优化的方向:密钥交换算法应该选择ECDHE算法而不是RSA算法,因为ECDHE算法具有前向安全性,客户端可以在第三次握手后发送加密的应用数据,节省1个RTT。将TSL1.2升级到TSL1.3,因为TSL1.3的握手过程只需要1个RTT,安全性更强。对于证书优化的方向:服务端应该选择ECDSA证书而不是RSA证书,因为在同等安全级别下,ECC的密钥长度比RSA短很多,可以提高证书传输的效率;服务端开启OCSPStapling功能,通过服务端提前获取OCSP响应并缓存响应结果,这样在TLS握手时就不需要访问CA服务器,减少网络通信开销,提高效率证书验证;当重新连接到HTTPS时,我们可以使用一些技术让客户端和服务器使用上次HTTPS连接时使用的会话密钥直接恢复会话,而不需要再次经过完整的TLS握手过程。常见的会话重用技术包括会话ID和会话票证。通过会话复用技术,当HTTPS再次重连时,只需要1个RTT就可以恢复会话。对于使用Pre-sharedKey会话重用技术的TLS1.3,恢复会话只需要0RTT。这些会话重用技术虽然使用方便,但存在一定的安全隐患。它们不仅没有前向安全性,而且还存在重放攻击的风险。因此,应该为会话密钥设置一个合理的过期时间。