# Https 性能优化


<!--more-->


根据 外媒Netmarketshare发布的数据显示，截止到2019 年 10 月全球使用HTTPS加密的 Web 流量的比例已经超过了九成。HTTPS网站加密传输协议几乎已经接近普及。

并且这个数据可以通过其它来源进行验证，根据 Google 透明度报告：
 “Chrome 中的 HTTPS 加密情况”来看，在谷歌这几年连推带吓的强制推广网站必须接入HTTPS协议的宣传下，到2019 年 10 月 Chrome 加载网页中启用加密的比例已经达到了 95%，几乎等于谷歌搜索内的网站已经全部普及。“

在全球范围，美国的 HTTPS 比例为 92%，俄罗斯 85%，日本 80%，印尼 74%，国内大约60%。

当下网站接入HTTPS加密传输协议，在全球已经基本成为一种普及的趋势，虽然国内很多站长对更换HTTPS还是有些排斥，但是对于搜索引擎推出的规则也只能遵守，对于站长们而言，网站接入HTTPS无非是早晚的事！



## 1.HTTPS 对访问速度的影响

HTTPS 对速度有什么影响。影响主要来自两方面：

- 协议交互所增加的网络 RTT(round trip time)(网络耗时）

  相对于http3次握手，https发起连接更复杂

  ![img_40](https://img.zhaojq.top/20260727091809498.png "img_40")

  而且很多用户习惯于使用HTTP请求你的网站，要保护用户的安全，首先要让用户强制302/301到HTTPS，这次跳转至少增加1个RTT的延时；301跳转后要再次TCP建连，又要增加1个RTT的延时。

- 加解密相关的计算耗时

  HTTPS为什么访问比较慢为什么消耗CPU资源呢？同样也是因为它很重。HTTPS的重，体现在如下几方面：

  - 大量的计算。SSL的每一个字节都涉及到较为复杂的计算。即使是client Hello，也需要在握手完成时做校验。
  - TLS协议的封装和解析。HTTPS所有数据都是按照TLS record格式进行封装和解析的。
  - 协议的网络交互。从TLS的握手过程可以看出，即使不需要进行任何计算， TLS的握手也需要至少2个RTT（round trip time)以上的网络交互。

  总体来说，HTTPS主要有如下计算环节：

  - 非对称密钥交换。这类算法的主要作用就是根据客户端和服务端不对称的信息，经过高强度的密钥生成算法，生成对称密钥，用于加解密后续应用消息。
  - 对称加解密。服务端使用密钥A对响应内容进行加密，客户端使用相同的密钥A对加密内容进行解密，反之亦然。
  - 消息一致性验证。每一段加密的内容都会附加一个MAC消息，即消息认证码。简单地说就是对内容进行的安全哈希计算，接收方需要校验MAC码。
  - 证书签名校验。这个阶段主要发生在客户端校验服务端证书身份时，需要对证书签名进行校验，确保证书的真实性。

  ![img_41](https://img.zhaojq.top/20260727091809926.png "img_41")



## 2.优化方案

### 2.1 tls握手过程分析

通过 Wireshark 抓包可以清楚地看到完整 TLS 握手过程所需的两个 RTT，如下图：

![img_42](https://img.zhaojq.top/20260727091810241.png "img_42")

client_key_exchange：合法性验证通过之后，客户端计算产生随机数字 Pre-master，并用证书公钥加密，发送给服务器;

### 2.2 False start

False Start 有抢跑的意思，意味着不按规则行事。TLS False Start 是指客户端在发送 Change Cipher Spec Finished 同时发送应用数据（如 HTTP 请求），服务端在 TLS 握手完成时直接返回应用数据（如 HTTP 响应）。这样，应用数据的发送实际上并未等到握手全部完成，故谓之抢跑。

浏览器在发送ChangeCipherSpec，并没有等待服务器端的确认就立即发送了加密应用数据，省去了一个RT。

False Start 相当于客户端提前发送加密后的应用数据，不需要修改 TLS 协议，目前大部分浏览器默认都会启用。

nginx开启false start 支持: 

`ssl_ciphers  ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!aNULL:!MD5;`

` ssl_prefer_server_ciphers on;`



### 2.3 升级到http2

HTTP/2 在底层传输做了很大的改动和优化

> 二进制分帧
> 首部压缩（Header Compression）多路复用
> 对请求划分优先级
> 服务器推送流（即Server Push技术）

Nginx 启用：`listen 443 ssl http2`;

### 2.4 会话恢复(session resumption)机制

SSL/TLS中的session跟HTTP的session类似，都是用来保存客户端和服务端之间交互的一些记录，这里的SSL的session保存的是SSL的握手记录。

TLS有几个特征可以用来消除额外的来回，比如重用一个会话session，两个标准会话重用机制是 session ID (RFC 5246) 和 session tickets (RFC 5077)，使用其中一个技术，一个客户端可以重用之前创建的会话，这个会话是之前和服务器进行握手成功的，这样可以减少一次来回过程。

**Session ID重用 / Session Cache**：
 一次TLS握手的结果是建立一条对称加密的数据通道，这条数据通道相关的参数都可以在内存中保存的，所以服务端就可以针对这一套参数值生成一个ID，叫做Session ID，使用该ID就可以直接复原对称加密的通信通道。所以当客户端下一次请求（Client Hello）到达的时候，客户端如果携带了Session ID，服务端就可以根据这个Session ID找到对应的Secure context，从而复原信道了。

 OpenSSL的Session Cache是不能够跨进程共享的，而Nginx是一个多进程的业务模型，如果每一个进程一份独立的Session Cache，就会导致同样一个SSL连接，上一次被一个Worker进程服务，下一次是被另外一个Worker进程服务的。使得整个OpenSSL在内存上造成极大的浪费，并且使得Cache的准确性大幅度下滑。

 所以Nginx就另外又实现了一套自己的Session Cache系统，这套系统是跨进程的，使用的是共享内存，用红黑树的方式组织的Session Cache，并且对Session ID的定义也重新进行了封装，最终形成了一个跨进程了Session Cache。

Session ID共享复用Nginx可以通过ssl_session_cache设置：` ssl_session_cache shared:SSL:10m;`

> 弊端：
>
> - 负载均衡中，多机之间往往没有同步 session 信息，如果客户端两次请求没有落在同一台机器上就无法找到匹配的信息；
> - 服务端存储 Session ID 对应的信息不好控制失效时间，太短起不到作用，太长又占用服务端大量资源。
>
> ssl_session_timeout 5m;



**Session Ticket**:

Session Ticket可以解决这些问题，Session Ticket 是用只有服务端知道的安全密钥加密过的会话信息，最终保存在浏览器端。浏览器如果在 ClientHello 时带上了 Session Ticket，只要服务器能成功解密就可以完成快速握手。

由于Session Ticket的客户端存储的特性，使得服务器完全就不需要管Session Cache方案需要面对的资源管理问题，唯一的代价就是一个解密计算的开销。同时，天然的支持了分布式，但是前提是分布式的所有节点都需要使用同样的服务器私有密钥（Session Ticket Encryption Key (STEK)），这无疑严重增加了危险系数。所以很多公司在实现的时候都会隔一段时间统一更换STEK。

```nginx
# 开启Session Ticket，此处没有指定加密算法，openssl默认会生成随机数的key。
# 如果需要手动指定，配置：ssl_session_ticket_key 
encode_decode.key;
# key文件可以由openssl命令生成，例如：openssl rand 80> ticket.key # 如果存在Nginx集群，多个集群应用使用同一份key文件，为保证安全性须每隔一定频率更换key。
ssl_session_tickets on;
```

---

在TLS1.3中，Session Cache和Session Ticket都被完全的取消，取而代之的是PSK（Pre Shared Key）。这个PSK并不是说每个客户端都要和服务端提前共享一个密钥，而是与握手相同的首先使用非对称加密方法直接提前协商一个密钥出来（psk_dhe_ke ），或者直接从之前协商出来的密钥参数中得出一个密钥（psk_ke）



### 2.5 HSTS

HSTS（HTTP Strict Transport Security，严格传输安全协议）国际互联网工程组织IETF正在推行一种新的Web安全协议。

采用HSTS协议的网站将保证浏览器始终连接到该网站的HTTPS加密版本，不需要用户手动在URL地址栏中输入加密地址。

HSTS的作用是强制客户端（如浏览器）使用HTTPS与服务器创建连接，而不是HTTP。

**实现原理**：

第一次通过HTTPS请求，服务器响应Strict-Transport-Security 头，以后尝试访问这个网站的请求都会自动把HTTP替换为HTTPS。

当HSTS头设置的过期时间到了，后面通过HTTP的访问恢复到正常模式，不会再自动跳转到HTTPS。

每次浏览器接收到Strict-Transport-Security头，它都会更新这个网站的过期时间，所以网站可以刷新这些信息，防止过期发生。

Chrome、Firefox等浏览器里，当您尝试访问该域名下的内容时，会产生一个307 Internal Redirect（内部跳转），自动跳转到HTTPS请求。

开启方法：

```nginx
#1. max-age：单位：秒。 HSTS header 过期时间，一般设置为1年，即31536000秒。
# 而每次Response Header都带上HSTS Header，则可不断刷新其过期时间。
#2. includeSubDomains：需要开启HSTS的域名/子域名。
# 在接下来的一年（即31536000秒）中，浏览器只要向xxx或其子域名发送HTTP请求时，必须采用HTTPS来发起连接。比如，用户点击超链接或在地址栏输入 http://xxx/ ，浏览器应当自动将 http 转写成 https，然后直接向https://xxx/ 发送请求。
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```

**避免中间人攻击**：

在公共场所，例如免费WIFI的环境中，第一次访问网站会弹出HSTS错误：

> 当你通过一个无线路由器的免费 WiFi 访问你的网银时，很不幸的，这个免费WiFi 也许就是由黑客的笔记本所提供的，他们会劫持你的原始请求，并将其重定向到克隆的网银站点，然后，你的所有的隐私数据都曝光在黑客眼下。
>
> 严格传输安全可以解决这个问题。如果你之前使用 HTTPS 访问过你的网银，而且网银的站点支持 HSTS，那么你的浏览器就知道应该只使用 HTTPS，无论你是否输入了 HTTPS，这样就防范了中间人劫持攻击。



### 2.6 OCSP stapling

- **Pki**:

  公钥基础设施(Public-Key Infrastructure)

  公钥基础设施是用来实现基于公钥密码体制的密钥和证书的产生、管理、存储、分发和撤销等功能。

- PKI的组成要素

  - 订阅人

    订阅人(或者说最终实体)是指那些需要证书来提供安全服务的团体

  - 登记机构

    登记机构(registration authority，RA)主要是完成一些证书签发的相关管理工作。例如， RA会首先对用户进行必要的身份验证，然后才会去找CA签发证书。在某些情况下，当 CA希望在用户附近建立一个分支机构时(例如在不同的国家建立当地登记中心)，我们也称RA为本地登记机构(localregistration authority，LRA)。实际上，很多CA也执行RA 的职责。

  - 证书颁发机构

    证书颁发机构(certification authority，CA)是指我们都信任的证书颁发机构，它会在确认申请用户的身份之后签发证书。同时CA会在线提供其所签发证书的最新吊销信息，这样信赖方就可以验证证书是否仍然有效。

  - 依赖方

    信赖方(relying party)是指那些证书使用者。技术上来说，一般是指那些执行证书验证的网页浏览器、其他程序以及操作系统。他们是通过维护根可信证书库来执行验证的， 这些证书库包含某些CA的最终可信证书(信任密钥，trust anchor)。更广泛地说，信赖方是指那些需要通过证书在互联网上进行安全通信的最终用户。

  ![img_43](https://img.zhaojq.top/20260727091810567.png "img_43")

- 证书链

- 浏览器证书检测 

  对于一个可信任的 CA 机构颁发的有效证书，在证书到期之前，只要 CA 没有把其吊销，那么这个证书就是有效可信任的。有时由于某些特殊原因（比如私钥泄漏，证书信息有误，CA 有漏洞被黑客利用，颁发了其他域名的证书等等），需要吊销某些证书。

  > 那浏览器或者客户端如何知道当前使用的证书已经被吊销了呢，通常有两种方式：
  >
  > - CRL（Certificate Revocation List，证书吊销列表）CRL 是由 CA 机构维护的一个列表，列表中包含已经被吊销的证书序列号（证书的唯一标识）和吊销时间。浏览器可以定期去下载这个列表用于校验证书是否已被吊销。而且当一个证书刚被吊销后，浏览器在更新 CRL 之前还是会信任这个证书的，实时性较差。
  >
  > - OCSP（Online Certificate Status Protocol，在线证书状态协议）具体实现是一个在线证书查询接口，它建立一个可实时响应的机制，让浏览器发送查询证书请求到CA服务器，然后CA服务器实时响应验证证书是否合法有效，这样可以实时查询每一张证书的有效性，解决了 CRL的实时性问题。
  >
  >   但是 OCSP 又有另外两个问题：CA服务器上的隐私和性能问题。由于OCSP要求浏览器直接请求第三方CA以确认证书的有效性，因此会损害隐私。
  >
  >   CA知道什么网站访问了该CA以及哪些用户访问了该网站。而这些数据对于跨国业务网站或者政企网站尤为敏感。
  >
  >   另一方面，某些客户端会在 SSL 握手时去实时查询 OCSP 接口，并在获得查询结果前会阻塞后续流程，在网络不佳时（尤其是内陆地区）会造成较长时间的页面空白，降低了HTTPS性能，严重影响用户体验。在服务器上部署OCSP Stapling将可以解决以上问题。

OCSP Stapling 就是为了解决 OCSP 性能问题而生的，**工作原理**：

网站服务器将自行查询OCSP服务器并缓存响应结果，然后在与浏览器进行TLS连接时将 OCSP 查询结果通过 Certificate Status 消息发送给浏览器，这样浏览器就不需要再去查询了。

浏览器客户端也不再需要向任何第三方披露用户的浏览习惯，完美解决了隐私问题。

当客户端向服务器发起 SSL 握手请求时，服务器将证书的 OCSP 信息随证书链一同发送给客户端，避免了客户端验证会产生的阻塞问题。

由于 OCSP 响应是无法伪造的，因此这一过程也不会产生额外的安全问题。

nginx开启oscp stapling: 

```nginx
# 开启 OCSP Stapling ---当客户端访问时 NginX 将去指定的证书中查找OCSP 服务的地址，获得响应内容后通过证书链下发给客户端。
ssl_stapling on;
# 启用OCSP响应验证，OCSP信息响应适用的证书
ssl_stapling_verify on;
# 指定完整的证书链
ssl_trusted_certificate /path/chain.pem;
# 根据Nginx文档，最好使用本地DNS服务，可以防止DNS欺骗(DNSspoofing)。使用公共的DNS服务，存在安全隐患。
# 添加resolver解析OSCP响应服务器的主机名，valid表示缓存。
resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=6000s;
# resolver_timeout表示网络超时时间
resolver_timeout 5s；
```


















