1. 程式人生 > >基於 Nginx 的 HTTPS 效能優化實踐

基於 Nginx 的 HTTPS 效能優化實踐

前言

分享一個卓見雲的較多客戶遇到HTTPS優化案例。

隨著相關瀏覽器對HTTP協議的“不安全”、紅色頁面警告等嚴格措施的出臺,以及向 iOS 應用的 ATS 要求和微信、支付寶小程式強制 HTTPS 需求,以及在合規方面如等級保護對傳輸安全性的要求都在推動 HTTPS 的發展。

雖然 HTTPS 優化了網站訪問體驗(防劫持)以及讓傳輸更加安全,但是很多網站主趕鴨子上架式的使用了 HTTPS 後往往都會遇到諸如:頁面載入速度變慢、伺服器負載過高以及證書過期不及時更新等問題。

所以本文就來探討一下 HTTPS 的優化實踐。

選型

其實像 Apache Httpd、LigHttpd、Canddy 等 Web 服務軟體都可以設定 HTTPS,但是在相應的擴充套件生態和更新率上都不如 Nginx。 Nginx 作為大型網際網路網站的 Web 入口軟體有著廣泛的支援率,例如阿里系的 Tengine、CloudFlare 的 cloudflare-nginx、又拍雲用的 OpenResty 都是基於 Nginx 而來的,Nginx 是接受過大規模訪問驗證的。同時大家也將自己開發的元件回饋給 Nginx 社群,讓 Nginx 有著非常良好的擴充套件生態。

image

​ 圖1-1 Nginx 在全網的使用情況

所以說 Nginx 是一款很好的 Web 服務軟體,選擇 Nginx 在提升效能的同時能極大的降低我們的擴充套件成本。

新功能

圍繞 Web 服務已經有非常多的新功能需要我們關注並應用了,這裡先羅列相關新功能。

HTTP/2

相比廉頗老矣的 HTTP/1.x,HTTP/2 在底層傳輸做了很大的改動和優化包括有:

  1. 每個伺服器只用一個連線,節省多次建立連線的時間,在TLS上效果尤為明顯
  2. 加速 TLS 交付,HTTP/2 只耗時一次 TLS 握手,通過一個連線上的多路利用實現最佳效能
  3. 更安全,通過減少 TLS 的效能損失,讓更多應用使用 TLS,從而讓使用者資訊更安全

image

在 Akamai 的 HTTP/2 DEMO中,載入300張圖片,HTTP/2 的優越性極大的顯現了出來,在 HTTP/1.X 需要 14.8s 的操作中,HTTP/2 僅需不到1s。

HTTP/2 現在已經獲得了絕大多數的現代瀏覽器的支援。只要我們保證 Nginx 版本大於 1.9.5 即可。當然建議保持最新的 Nginx 穩定版本以便更新相關補丁。同時 HTTP/2 在現代瀏覽器的支援上還需要 OpenSSL 版本大於 1.0.2。

TLS 1.3

和 HTTP/1.x 一樣,目前受到主流支援的 TLS 協議版本是 1.1 和 1.2,分別釋出於 2006年和2008年,也都已經落後於時代的需求了。在2018年8月份,IETF終於宣佈TLS 1.3規範正式釋出了,標準規範(Standards Track)定義在

rfc8446

image
image

TLS 1.3 相較之前版本的優化內容有:

  1. 握手時間:同等情況下,TLSv1.3 比 TLSv1.2 少一個 RTT
  2. 應用資料:在會話複用場景下,支援 0-RTT 傳送應用資料
  3. 握手訊息:從 ServerHello 之後都是密文。
  4. 會話複用機制:棄用了 Session ID 方式的會話複用,採用 PSK 機制的會話複用。
  5. 金鑰演算法:TLSv1.3 只支援 PFS (即完全前向安全)的金鑰交換演算法,禁用 RSA 這種金鑰交換演算法。對稱金鑰演算法只採用 AEAD 型別的加密演算法,禁用CBC 模式的 AES、RC4 演算法。
  6. 金鑰匯出演算法:TLSv1.3 使用新設計的叫做 HKDF 的演算法,而 TLSv1.2 是使用PRF演算法,稍後我們再來看看這兩種演算法的差別。

總結一下就是在更安全的基礎上還做到了更快,目前 TLS 1.3 的重要實現是 OpenSSL 1.1.1 開始支援了,並且 1.1.1 還是一個 LTS 版本,未來的 RHEL8、Debian10 都將其作為主要支援版本。在 Nginx 上的實現需要 Nginx 1.13+。

Brotli

Brotli 是由 Google 於 2015 年 9 月推出的無失真壓縮演算法,它通過用變種的 LZ77 演算法,Huffman 編碼和二階文字建模進行資料壓縮,是一種壓縮比很高的壓縮方法。

根據Google 釋出的研究報告,Brotli 具有如下特點:

  1. 針對常見的 Web 資源內容,Brotli 的效能要比 Gzip 好 17-25%;
  2. Brotli 壓縮級別為 1 時,壓縮速度是最快的,而且此時壓縮率比 gzip 壓縮等級為 9(最高)時還要高;
  3. 在處理不同 HTML 文件時,brotli 依然提供了非常高的壓縮率;

在相容 GZIP 的同時,相較 GZIP:

  1. JavaScript 上縮小 14%
  2. HTML上縮小 21%
  3. CSS上縮小 17%

Brotli 的支援必須依賴 HTTPS,不過換句話說就是隻有在 HTTPS 下才能實現 Brotli。

ECC 證書

橢圓曲線密碼學(Elliptic curve cryptography,縮寫為ECC),一種建立公開金鑰加密的演算法,基於橢圓曲線數學。橢圓曲線在密碼學中的使用是在1985年由Neal Koblitz和Victor Miller分別獨立提出的。

內建 ECDSA 公鑰的證書一般被稱之為 ECC 證書,內建 RSA 公鑰的證書就是 RSA 證書。由於 256 位 ECC Key 在安全性上等同於 3072 位 RSA Key,加上 ECC 運算速度更快,ECDHE 金鑰交換 + ECDSA 數字簽名無疑是最好的選擇。由於同等安全條件下,ECC 演算法所需的 Key 更短,所以 ECC 證書檔案體積比 RSA 證書要小一些。

ECC 證書不僅僅可以用於 HTTPS 場景當中,理論上可以代替所有 RSA 證書的應用場景,如 SSH 金鑰登陸、SMTP 的 TLS 發件等。

不過使用 ECC 證書有兩個點需要注意:

一、 並不是每一個證書型別都支援的,一般商業證書中帶增強型字眼的才支援ECC證書的簽發。

image

二、 ECC證書在一些場景中可能還不被支援,因為一些產品或者軟體可能還不支援 ECC。 這時候就要虛線解決問題了,例如針對部分舊作業系統和瀏覽器不支援ECC,可以通過ECC+RSA雙證書模式來解決問題。

安裝

下載原始碼

綜合上述我們要用到的新特性,我們整合一下需求:

HTTP/2 要求 Nginx 1.9.5+,,OpenSSL 1.0.2+

TLS 1.3 要求 Nginx 1.13+,OpenSSL 1.1.1+

Brotli 要求 HTTPS,並在 Nginx 中新增擴充套件支援

ECC 雙證書 要求 Nginx 1.11+

這裡 Nginx,我個人推薦 1.15+,因為 1.14 雖然已經能支援TLS1.3了,但是一些 TLS1.3 的進階特性還只在 1.15+ 中提供。

然後我們定義一下版本號:

# Version
OpenSSLVersion='openssl-1.1.1a';
nginxVersion='nginx-1.14.1';

建議去官網隨時關注最新版:

http://nginx.org/en/download.html

https://www.openssl.org/source/

https://github.com/eustas/ngx_brotli/releases

Nginx

cd /opt
wget http://nginx.org/download/$nginxVersion.tar.gz
tar xzf $nginxVersion.tar.gz

OpenSSL

cd /opt
wget https://www.openssl.org/source/$OpenSSLVersion.tar.gz
tar xzf $OpenSSLVersion.tar.gz

Brotli

cd /opt
git clone https://github.com/eustas/ngx_brotli.git
cd ngx_brotli
git submodule update --init --recursive

編譯

cd /opt/$nginxVersion/
./configure \
--prefix=/usr/local/nginx \  ## 編譯後安裝的目錄位置
--with-openssl=/opt/$OpenSSLVersion  \ ## 指定單獨編譯入 OpenSSL 的原始碼位置
--with-openssl-opt=enable-tls1_3 \ ## 開啟 TLS 1.3 支援
--with-http_v2_module \ ## 開啟 HTTP/2 
--with-http_ssl_module \ ## 開啟 HTTPS 支援
--with-http_gzip_static_module \ ## 開啟 GZip 壓縮
--add-module=/opt/ngx_brotli ## 編譯入 ngx_BroTli 擴充套件

make && make install ## 編譯並安裝

後續還有相關變數設定和設定服務、開啟啟動等步驟,篇幅限制就省略了,這篇文章有介紹在 Ubuntu 下的 Nginx 編譯:https://www.mf8.biz/ubuntu-nginx/

配置

接下來我們需要修改配置檔案。

HTTP2

listen 443 ssl http2;

只要在 server{} 下的lisen 443 ssl 後新增 http2 即可。而且從 1.15 開始,只要寫了這一句話就不需要再寫 ssl on 了,很多小夥伴可能用了 1.15+ 以後衍用原配置檔案會報錯,就是因為這一點。

TLS 1.3

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

如果不打算繼續支援 IE8,或者一些合規的要求,可以去掉TLSv1

然後我們再修改對應的加密演算法,加入TLS1.3引入的新演算法:

ssl_ciphers        TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;

如果不打算繼續支援 IE8,可以去掉包含 3DES 的 Cipher Suite。

預設情況下 Nginx 因為安全原因,沒有開啟 TLS 1.3 0-RTT,可以通過新增 ssl_early_data on; 指令開啟 0-RTT的支援。

————

實驗性嘗試

眾所周知,TLS1.3 由於更新了很久,很多瀏覽器的舊版本依舊只支援 Draft 版本,如 23 26 28 分別在 Chrome、FirFox 上有支援,反而正式版由於草案出來很久,導致TLS1.3在瀏覽器上相容性不少太好。

可以使用 https://github.com/hakasenyang/openssl-patch/ 提供的 OpenSSL Patch 讓 OpenSSL 1.1.1 同時支援草案23,26,28和正式版輸出。 不過由於不是官方指令碼,穩定性和安全性有待考量。

ECC雙證書

雙證書配置的很簡單了,保證域名的證書有RSA和ECC各一份即可。

  ##證書部分
  ssl_certificate /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.crt; #ECC證書
  ssl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.key; #ECC金鑰
  ssl_certificate /usr/local/nginx/conf/ssl/www.mf8.biz.crt; #RSA證書
  ssl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz.key; #RSA金鑰

Brotli

需要在對應配置檔案中,新增下面程式碼即可:

    brotli                     on;
    brotli_comp_level          6;
    brotli_min_length          1k;
    brotli_types               text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype;

為了防止大家看糊塗了,放一個完整的 server{}供大家參考:

    server {
        listen       443 ssl http2; # 開啟 http/2
        server_name  mf8.biz www.mf8.biz;
    
        #證書部分
        ssl_certificate     /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.crt; #ECC證書
        ssl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.key; #ECC金鑰
        ssl_certificate     /usr/local/nginx/conf/ssl/www.mf8.biz.crt; #RSA證書
        sl_certificate_key  /usr/local/nginx/conf/ssl/www.mf8.biz.key; #RSA金鑰
        
        #TLS 握手優化
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        keepalive_timeout    75s;
        keepalive_requests   100;
        
        #TLS 版本控制
        ssl_protocols    TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers        'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5';

        # 開啟 1.3 o-RTT
        ssl_early_data     on;
    
        # GZip 和 Brotli
        gzip            on;
        gzip_comp_level    6;
        gzip_min_length    1k;
        gzip_types        text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype;
        brotli            on;
        brotli_comp_level    6;
        brotli_min_length    1k;
        brotli_types    text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }

先驗證一下配置檔案是否有誤:

nginx -t

如果反饋的是:

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

就可以重啟 Nginx ,然後到對應網站中去檢視效果了。

驗證

HTTP/2

通過瀏覽器的開發者工具,我們可以在 Network 欄目中看到 Protocol 中顯示 h2 有無來判斷。

image

TLS 1.3

老地方,我們可以通過瀏覽器的開發者工具 中的 Security 欄目看到 Connection 欄目下是否有顯示 TLS 1.3

image

ECC 雙證書

ECC 雙證書配置了以後無非就是在舊瀏覽器設別上的驗證了。這裡用足夠老的上古XP虛擬機器來給大家證明一波。

XP系統上:

image

現代作業系統上的:

image

Brotli

通過瀏覽器的開發者工具,我們可以在 Network 欄目中,開啟具體頁面的頭資訊,看到 accept-encoding 中有 br 字眼就行。

image

總結

通過上述手段應該可以讓 HTTPS 訪問的體驗優化不少,而且會比沒做 HTTPS 的網站訪問可能更快。

這樣的模式比較適合雲伺服器單機或者簡單叢集上搭建,如果有應用 SLB 七層代理、WAF、CDN 這樣的產品可能會讓我們的這些操作都白費。 我們的這幾項操作都是自建的 Web 七層服務,如果有設定 SLB 七層代理、WAF、CDN 這樣設定在雲伺服器之前就會被覆蓋掉。

由於 SLB 七層和CDN這樣的產品會更加追求廣泛的相容性和穩定性並不會第一時間就用上上述的這些新特性(HTTP/2 是普遍有的),但是他們都配備了阿里雲的 Tengine 的外部專用演算法加速硬體如 Intel® QuickAssist Technology(QAT) 加速器可以顯著提高SSL/TLS握手階段效能。 所有 HTTPS 的加密解密都在 SLB 或 CDN 上完成,而不會落到ECS上,可以顯著降低 ECS 的負載壓力,並且提升訪問體驗。

目前雲上的網路產品中能支援四層的都是可以繼續相容我們這套設計的,例如:SLB 的四層轉發(TCP UDP)、DDOS高防的四層轉發。

也歡迎大家有問題來找卓見雲,聊騷也可以呀~。 我的釘釘號:huicute , 以及我的部落格:https://www.mf8.biz