阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

HTTPS 性能优化技巧

33次阅读
没有评论

共计 7008 个字符,预计需要花费 18 分钟才能阅读完成。

认识 SSL/TLS

SSL 和 TLS 都是用于保障端到端之间连接的安全性。SSL 最初是由 Netscape 开发的,后来为了使得该安全协议更加开放和自由,更名为 TLS,并被标准化到 RFC 中,现在主流的是 TLS 1.2 版本。

HTTPS 性能优化技巧

从上图,可以看出 SSL/TLS 是介于应用层和传输层之间,并且分为握手层(Handshake Layer)和记录层(Record Layer)。

  • 握手层:端与端之间协商密码套件、连接状态。
  • 记录层:对数据的封装,数据交给传输层之前,会经过分片 - 压缩 - 认证 - 加密。
算法选择

TLS 中可被配置的算法分类:

  1. 数字签名:RSA、DSA
  2. 流加密:RC4
  3. 分组加密:DES、AES
  4. 认证加密:GCM
  5. 公钥加密:RSA
  6. 消息认证码:SHA
  7. 密钥交换:Diffie–Hellman

密码套件决定了会使用到的算法,例如执行“openssl ciphers -v ‘ALL’ | grep ECDHE-RSA-AES128-GCM-SHA256”:

ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD

表明该算法是在 TLS 1.2 中支持的,密钥交换采用 ECDH(EC 是指采用椭圆曲线的 DH), 数字签名采用 RSA,加密采用 128 位密钥长度的 AESGCM,消息认证码采用 AEAD(AEAD 是一种新的加密形式,把加密和消息认证码结合到一起,而不是某个算法,例如使用 AES 并采用 GCM 模式加密,就能够为数据提供保密性、完整性的保障)。
如何理解完整性?
A 将明文 M 加密后为 MC,发给 B,B 解密,得到明文。如果此时有中间人 C,将 MC 替换为 CMC(虽然 C 不知道 A 怎么加密的,但这没关系),B 将 CMC 解密,得到明文(那么 B 拿到的其实是错误的明文)。所以需要引入消息认证码,B 才能够判断收到的密文是否被篡改过。这里你可能会问:那如果 C 同时伪造消息认证码呢?这个就得看 MAC 和加密是如何配合的了,详情可以查看认证加密中的 Approaches to Authenticated Encryption 章节。

在 TLS 握手和数据传输的不同阶段会采用相应的算法:

  • 服务端身份验证:数字签名(RSA、ECDSA)
  • 密钥交换:RSA/ 密钥交换算法(ECDH)
  • 加密 / 解密:流加密(RC4)和分组加密(3DES/AES/AESGCM)
  • 生成消息认证码:SHA/AEAD

不知是否有人发现并没有提到压缩算法,如果 google 下 TLS 压缩优化相关的内容,会发现没有,因为目前在 TLS 1.2 RFC 中,关于压缩方法的结构定义为 enum {null(0), (255) } CompressionMethod;,即只有 null 方法(不进行压缩)。目前存在对 TLS 压缩的攻击,可能是基于此原因,TLS 压缩目前只是个概念性的东西,没有被真正应用起来。

如何选择算法——安全性

通常加密算法的安全性依赖于密钥的长度,且不同加密算法,即使密钥长度相同,但提供的安全性也可能是不同的,相关资料:key size。所以并没有一个标准的归一化方法去衡量所有的加密算法,但是有来自世界上各个组织 / 机构对不同类型算法安全性的评估,可以看下这个网站:https://www.keylength.com/。

执行“openssl ciphers -v ‘ALL’ | wc -l”会发现有 100+ 个密码套件(不同 openssl 版本提供的密码套件有点差异),然而,实际只会使用到其中一部分,因为 openssl 提供的不少算法是不安全的,需要排除掉。

执行“openssl ciphers -v ‘HIGH MEDIUM !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !CAMELLIA !IDEA !SEED !RC4’ | wc -l”,发现只剩下 50+ 个密码套件。

筛选后剩下的密码套件还是挺多的,一个个做性能测试的话,会 GG 的 = =。其实可以根据需要支持的客户端,再筛选出主流的密码套件。网址:https://www.ssllabs.com/ssltest/clients.html,提供了绝大部分客户端对 TLS 的支持情况,点击相应的 User agent 可以查看到其支持的密码套件,并且各套件的安全性也被标注出来了。

网址:https://www.ssllabs.com/ssltest/,可以用于测试服务器的 SSL 配置情况,并会给出得分,如下图 google 的得分为 A:

HTTPS 性能优化技巧

如何选择算法——性能

以下性能测试都是选取主流的算法进行。

数字签名:ECDSA vs RSA

需要先分别生成采用 ECDSA 和 RSA 的签名证书。

生成 ECDSA 自签名的证书:

openssl ecparam -name prime256v1 -genkey -out ec_key.pem
openssl req -new -x509 -key ec_key.pem -out cert.pem -days 365

-param_enc 参数使用默认的 named_curve 就可以了,如果使用 explicit,会发现生成的证书 nginx 能配置成功,但客户端连接时会出现 handshake error。
生成 RSA 签名的证书:

openssl req -newkey rsa:2048 -nodes -keyout rsa_key.pem -x509 -days 365 -out cert.pem

执行 openssl speed rsa2048 ecdsap256 测试下:

sign    verify    sign/s verify/s
rsa 2048 bits 0.000834s 0.000024s   1198.9  41031.9
                              sign    verify   sign/s  verify/s
256 bit ecdsa (nistp256)   0.0000s   0.0001s  21302.5   9728.5

可以看到签名性能 ECDSA > RSA,而验证性能 RSA > ECDSA。

测试环境:

  • 服务端:1 台虚拟机 CentOS 4 核 openresty 2 个 worker
  • 客户端:4 台虚拟机 CentOS 4/2/2/ 2 核(手头只有这些虚拟机 = =),用 shell 脚本模拟并发的 ab -c 800 -n 800(并发的 ab 实例数 =2*CPU_NUM),使用 time 命令获取消耗的时间
  • 测试页面 562 字节,目标是测试数字签名的性能,所以页面小点,避免加密 / 解密、数据传输占用太多时间

多台客户端如何同时启动?ctrl+tab,命令 + 回车……
为什么不用 jmeter?我用了 1Master3Slave 的 jmeter 分布式压测发现,jmeter 对于在该场景(CPU bound)下的性能测试不行,服务端压力上不去。
在相同的请求量下,RSA 签名会使服务端 CPU 占用更高,所以这次测试需要在两种签名的压测下,服务端 CPU 都保持在 90% 以上(不然的话,对 ECDSA 就不公平了)。
为何 openresty 是 2 个 worker?因为开 4 个的话,ECDSA 的压测没法使 openresty4 个 worker 的 CPU 消耗达到 90%。
ECDHE-ECDSA-AES128-GCM-SHA256,服务端 CPU 占比 90%,结果:

客户端(CPU 核数标识) 4 2 2 2
第一次 11.988 17.334 9.161 7.748
第二次 12.524 13.750 12.129 7.582
第三次 11.836 17.991 9.195 10.023
第四次 11.617 7.081 9.168 8.919

ECDHE-RSA-AES128-GCM-SHA256,服务端 CPU 占比 100%,结果:

客户端(CPU 核数标识) 4 2 2 2
第一次 12.704 21.088 18.232 6.134
第二次 13.355 21.071 26.990 6.102
第三次 14.638 16.009 11.669 6.071
第四次 13.913 21.061 21.271 5.108

从表格中的数据可以看出 ECDSA 的性能要比 RSA 好点,这里 ECDSA 的测试尚未压榨完服务端呢。从 openssl speed 的结果也可以看出 ECDSA 的签名性能是要远超过 RSA 的,而且签名是在服务端做的,所以面对海量的客户端,服务端应该选择使用 ECDSA。

密钥交换:RSA vs ECDHE

测试环境同上,但只使用了 4 / 2 核两台客户端机器发请求。证书使用的是生成的 RSA 证书,ECDSA 证书能用到的密钥交换算法只能是 ECDHE。

AES256-GCM-SHA384,服务端 CPU 占比 100%,结果:

客户端(CPU 核数标识) 4 2
第一次 12.144 15.737
第二次 12.133 15.452
第三次 11.902 16.145
第四次 11.614 16.133

ECDHE-RSA-AES256-GCM-SHA384,服务端 CPU 占比 100%,结果:

客户端(CPU 核数标识) 4 2
第一次 11.950 16.213
第二次 12.488 16.666
第三次 12.167 16.378
第四次 13.784 16.484

从表格中的数据可以看出 ECDHE 与 RSA 的性能差不多。ECDHE 比 RSA 要多了一次端到端的传输,还会用到 RSA 对 DH 参数进行签名和验证;而 RSA 密钥交换则会使用到 RSA 的加密 / 解密,具体可看如下 CloudFlare 的两张图,图片来自 Keyless SSL: The Nitty Gritty Technical Details:
ECDHE 支持前向保密(Forward Secrecy),简单理解:中间人可以保存下来客户端和服务端之间的所有通信数据,如果使用 RSA 握手,那么未来某一天,中间人如果获取到了服务端的私钥,就可以解密所有之前采集的通信数据了;如果采用 ECDHE 握手的话,就可以避免这个问题。而且使用 ECDHE 握手的话,还有可能开启 TLS false start 的特性(下文中会提到)。
RSA 握手:

HTTPS 性能优化技巧

ECDHE 握手:

HTTPS 性能优化技巧

所以密钥交换算法 ECDHE 会更好些。

对称加密:AES256-GCM vs AES256 vs AES128-GCM vs 3DES

测试环境同上,但只使用了 4 核一台客户端机器发请求,ab 参数为 ab -n 2000 -c 10,ab 实例 4 个,测试页面 153K。因为是要压测对应用层数据的加密解密性能,所以连接数少,但每个连接的请求数多。

ECDHE-RSA-AES256-GCM-SHA384,服务端 CPU 占比 94%,结果:

客户端(CPU 核数标识) 4
第一次 17.972
第二次 18.863
第三次 18.761
第四次 19.345

ECDHE-RSA-AES256-SHA384,服务端 CPU 占比 98%,结果:

客户端(CPU 核数标识) 4
第一次 20.490
第二次 19.575
第三次 19.725
第四次 20.262

ECDHE-RSA-AES128-GCM-SHA256,服务端 CPU 占比 92%,结果:

客户端(CPU 核数标识) 4
第一次 17.886
第二次 18.449
第三次 17.897
第四次 18.371

DES-CBC3-SHA,服务端 CPU 占比 100%,结果(太慢了,就测了两个 =。=):

客户端(CPU 核数标识) 4
第一次 52.262
第二次 51.476

从表格中的数据可以看出 AES128GCM > AES256GCM > AES256 > 3DES。

消息认证码:SHA256 vs SHA1 vs AEAD

测试环境同上。

AES256-SHA256,服务端 CPU 占比 100%,结果:

客户端(CPU 核数标识) 4
第一次 18.544
第二次 18.309
第三次 18.594
第四次 18.670

AES256-SHA,服务端 CPU 占比 98%,结果:

客户端(CPU 核数标识) 4
第一次 15.418
第二次 15.071
第三次 16.614
第四次 16.146

AES256-GCM-SHA384,服务端 CPU 占比 95%,结果:

客户端(CPU 核数标识) 4
第一次 14.443
第二次 15.669
第三次 15.880
第四次 15.960

从结果中可以看出 AES256-GCM-SHA384 > AES256-SHA > AES256-SHA256。

会话恢复

Session Cache

客户端希望恢复先前的 session,或者复制一个存在的 session,可以在 ClientHello 中带上 Session ID,如果服务端能够在它的 Session Cache 中找到相应的 Session ID 的 session-state(存储协商好的密码套件等信息),并且愿意使用该 Session ID 重建连接,那么服务端会发送一个带有相同 Session ID 的 ServerHello。

HTTPS 性能优化技巧

目前 Nginx 只支持单机 Session Cache,Openresty 支持分布式 Session Cache,但处于实验阶段。

Session Ticket

Session Cache 需要服务端缓存 Session 相关的信息,对服务端存在存取压力,而且还有分布式 Session Cache 问题。对于支持 Session Ticket 的客户端,服务端可以通过某种机制将 session-state 加密后作为 ticket 发给客户端。客户端凭借该 ticket 就可以恢复先前的会话了。
类似于 HTTP 中用 Json Web TOken 作为 cookie-session 的另一种选择。
HTTPS 性能优化技巧

OCSP(在线证书状态协议)stapling

当客户端在握手环节接受到服务端的证书时,除了对证书进行签名验证,还需要知道证书是否被吊销了,那么需要向证书中指定的 OCSP url 发送 OCSP 查询请求。

HTTPS 性能优化技巧

对于同一份服务端证书,如果每个客户端都自己去查询一次证书状态就浪费了。所以,OCSP stapling 就是为了解决这一问题,由服务端查询到证书状态(通常会缓存一段时间),并返回给客户端(客户端会在本地校验这个证书状态是否真实)。

HTTPS 性能优化技巧

在 nginx 的配置中,可以选择性的配置是否对 OCSP response 做校验,防止将非法的证书状态发送给客户端。如果设置了校验,ssl_trusted_certificate 参数需要为包含所有中间证书 + 根证书的文件。

如下图是对 nginx 请求 OCSP Server 的抓包,可以看到发了个 http 的 ocsp 请求:

HTTPS 性能优化技巧

下图是对 nginx 在发送证书给客户端时,带上的证书状态的抓包:

HTTPS 性能优化技巧

TLS 缓冲区调优

nginx 默认的 ssl_buffer_size 是 16K(TLS Record Layer 最大的分片),即一个 TLS Record 的大小,如果 HTTP 的数据是 160K,那么就会被拆分为 10 个 TLS Record(每个 TLS Record 会被 TCP 层拆分为多个 TCP 包传输)发送给客户端。

HTTPS 性能优化技巧

如果 TLS Record Size 过大的话,拆分的 TCP 包也会较多,传输时,如果出现 TCP 丢包,整个 TLS Record 到达客户端的时间就会加长,客户端必须等待完整的 TLS Record 收到才能进行解密。

HTTPS 性能优化技巧

如果 TLS Record Size 小一些的话,TCP 丢包影响的 TLS Record 占比就会小很多,到达客户端的 TLS Record 就会多些,客户端干等着的时间就相对少了。但是,TLS Record Head 的负载就增加了,可能还会降低连接的吞吐量。

假设 ssl_buffer_size 设置为 1460byte:

HTTPS 性能优化技巧

通常,在 TCP 慢启动的过程中,TLS Record Size 小点好,因为这个时候 TCP 连接的拥塞窗口 cwnd 较小,TCP 连接吞吐量也小。而在 TCP 连接结束慢启动之后,TLS Record Size 就可以增大一些了,因为这个时候吞吐量上来了。所以更希望能够动态的调整 nginx 中 ssl_buffer_size 的大小,目前官方 nginx 还不支持,不过 cloudflare 为 nginx 打了个 patch,以支持动态的调整 TLS Record Size:Optimizing TLS over TCP to reduce latency。

TLS False Start

某一端在发送 Change Cipher Spec、Finished 之后,可以立即发送应用数据,无需等待另一端的 Change Cipher Spec、Finished。这样,应用数据的发送实际上并未等到握手全部完成,从而节省出一个 RTT 时间。

完整握手时,Client Side False Start:

HTTPS 性能优化技巧

简短握手时,Server Side False Start:

HTTPS 性能优化技巧

Client Side False Start 需要的条件:

  • 客户端和服务端都需要支持 NPN/ALPN(浏览器要求)
  • 需要采用支持前向保密的密码套件,即使用 ECDHE 进行密钥交换(RFC7918 中有规定)
其他优化
  • TCP 优化,毕竟 SSL 数据也是基于 TCP 进行传输的
  • 证书优化,采用 ECDSA 证书、服务器发送给客户端的证书链包含所有中间证书
  • 硬件配置优化,例如使用 SSL 加速器
总结

本文是个人近段时间学习到的关于 HTTPS 性能优化的总结,推荐阅读 HTTPS 权威指南和 High Performance Browser Networking 以了解更多内容。

推荐的密码套件列表:

openssl ciphers -v 'ECDHE+ECDSA ECDHE AESGCM AES HIGH MEDIUM !kDH !kECDH !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !CAMELLIA !IDEA !SEED !RC4 !3DES'

其他额外的密码套件,比如需要支持 IE6,可以放在密码套件列表末尾。

自己写了个 go 程序用于检测密码套件列表支持 / 不支持的客户端:sslciphersuitescheck

HTTPS 性能优化技巧

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-24发表,共计7008字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中