本文邀請阿里雲CDN HTTPS技術專家金九,分享Tengine的一些HTTPS實踐經驗。內容主要有四個方面:HTTPS趨勢、HTTPS基礎、HTTPS實踐、HTTPS調試。
一、HTTPS趨勢
這一章節主要介紹近幾年和未來HTTPS的趨勢,包括兩大瀏覽器chrome和firefox對HTTPS的態度,以及淘寶天貓和阿里雲CDN的HTTPS實踐情況。
上圖是 chrome 統計的HTTPS網頁佔比的趨勢,2015年的時候,大多數國家的HTTPS網頁加載次數只佔了不到 50%,2016年美國這個佔比到了將近60%,去年就已經超過 70%,目前已經超過 80%。最下面是日本,目前是60%左右,這裡面沒有統計到中國的數據,我估計中國的佔比會更少,空間還有很大,但未來HTTPS趨勢是明顯的。
同樣,Firefox 瀏覽器加載HTTPS網頁的統計跟 chrome 差不多,全球 HTTPS網頁加載佔比在 70% 左右,上升趨勢也很明顯。
更值得關注的是,Google 在今年年初的時候在其安全博客上表明,在今年7月份左右發佈的 chrome68 瀏覽器會將所有HTTP網站標記為不安全,現在是5月份底了,離這個時間也就一個多月的時間了。右邊的截圖可以看出本來是綠色的小鎖變成了不安全,這種網頁在輸入密碼時就很不安全了。
早期天貓淘寶只是在關鍵的登錄和交易的環節上了HTTPS,但隨著互聯網的發展,劫持、篡改等等問題也越來越嚴重,試想在天貓淘寶上看的商品圖片被惡意人替換了或者價格被篡改了會怎麼樣?這樣用戶、商家和平臺都受到傷害,只有上了 HTTPS才能從根本上解決這些問題。所以,天貓和淘寶在2015年7月份的時候已經完成了全站HTTPS。
二、HTTPS基礎
本章節主要介紹HTTPS為什麼安全,包括對稱加密、非對稱加密、簽名、證書&證書鏈、SSL是怎麼握手的、以及私鑰密鑰在HTTPS中發揮什麼樣的作用、keyless又是解決什麼樣的問題等等。
HTTPS的定義
簡單來講,HTTPS就是安全的HTTP,我們知道HTTP是運行在TCP層之上的,HTTPS在HTTP層和TCP層之間加了一個SSL層,SSL向上提供加密和解密的服務,對HTTP比較透明,這樣也便於服務器和客戶端的實現以及升級。
HTTPS為什麼安全?
HTTPS安全是由一套安全機制來保證的,主要包含這4個特性:機密性、完整性、真實性和不可否認性。
- 機密性是指傳輸的數據是採用Session Key(會話密鑰)加密的,在網絡上是看不到明文的。
- 完整性是指為了避免網絡中傳輸的數據被非法篡改,使用MAC算法來保證消息的完整性。
- 真實性是指通信的對方是可信的,利用了PKI(Public Key Infrastructure 即『公鑰基礎設施』)來保證公鑰的真實性。
- 不可否認性是這個消息就是你給我發的,無法偽裝和否認,是因為使用了簽名的技術來保證的。
對稱加密和非對稱加密
HTTPS有對稱加密和非對稱加密兩種算法,目的都是把明文加密成密文,區別是密鑰的個數不一樣,對稱加密是一把密鑰,這把密鑰可以加密明文,也可以解密加密後的密文,常見的對稱加密算法有AES、DES、RC4,目前最常用的是AES。
非對稱加密是兩把密鑰,分別是公鑰和私鑰,公鑰加密的密文只有相對應的私鑰才能解密,私鑰加密的內容也只有相對應的公鑰才能解密,其中公鑰是公開的,私鑰是自己保存,不能公開,常見的非對稱加密算法有RSA和ECC(橢圓曲線算法)。
SSL結合了這兩種加密算法的優點,通過非對稱加密來協商對稱加密的密鑰,握手成功之後便可使用對稱加密來做加密通信,對於RSA來說,客戶端是用RSA的公鑰把預主密鑰加密後傳給服務器,服務器再用私鑰來解密,雙方再通過相同的算法來生成會話密鑰,之後的應用層數據就可以通過會話密鑰來加密通信。
簽名
SSL中還有一個使用非對稱加密的地方就是簽名,簽名的目的是讓對方相信這個數據是我發送的,而不是其他人發送的。
密鑰安全強度與性能對比
加密之後的數據破解難度就體現在密鑰的長度上,密鑰越長,破解難度也越大,但是運算的時間也越長,性能也就越差。相同安全強度下,對稱密鑰長度在最短,ECC次之,RSA密鑰長度則最長。
目前比較常用的密鑰長度是:對稱密鑰128位、ECC 256位、RSA 2048位。
RSA和ECC在SSL中更多的是用來簽名,通過測試發現,ECC 的簽名性能比RSA好很多,但是RSA的驗籤性能比ECC更好,所以RSA更適合於驗證簽名頻繁而簽名頻度較低的場景,ECC更適合於簽名頻繁的場景,在SSL場景中,ECC算法性能更好。
公鑰基礎設施(PKI)
簡單的說,PKI就是瀏覽器和CA,CA是整個安全機制的重要保障,我們平時用的證書就是由CA機構頒發,其實就是用CA的私鑰給用戶的證書籤名,然後在證書的簽名字段中填充這個簽名值,瀏覽器在驗證這個證書的時候就是使用CA的公鑰進行驗籤。
證書
那CA在PKI中又是怎樣發揮作用的呢,首先,CA的作用就是頒發證書,頒發證書其實就是使用CA的私鑰對證書請求籤名文件進行簽名,其次,CA頒發的證書瀏覽器要信任,瀏覽器只需要用CA的公鑰進行驗籤成功就表示這個證書是合法可信的,這就需要瀏覽器內置CA的公鑰,也就是內置CA的證書。一般來說,操作系統都會內置權威CA的證書,有的瀏覽器會使用操作系統內置的CA證書列表,有的瀏覽器則自己維護的CA證書列表,比如Firefox。
CA分為根CA,二級CA,三級CA,三級CA證書由二級CA的私鑰簽名,二級CA證書由根CA的私鑰簽名,根CA是自簽名的,不會給用戶證書籤名,我們平時用的證書都是由二級CA或者三級CA簽名的,這樣就形成了一個證書鏈,瀏覽器在驗籤的時侯一層層往上驗證,直到用內置的根CA證書的公鑰來驗籤成功就可以表示用戶證書是合法的證書。
根證書已經內置在瀏覽器或者操作系統裡了,在SSL握手時就不需要髮根CA證書了,只需要提供中間二級三級CA證書和用戶證書就可以。
交叉證書
交叉證書的應用場景是這樣的:假如現在阿里雲成為一個新的根CA機構,那阿里雲簽發的證書想要瀏覽器信任的話,阿里雲的根證書就需要內置在各大操作系統和瀏覽器中,這需要較長時間的部署,那在沒有完全部署完成之前,阿里雲簽發的證書怎麼才能讓瀏覽器信任呢,這就需要用到交叉證書了。
上圖中,根證書A是一個所有瀏覽器都內置了的根證書,B是一個新的根證書,用戶證書D是由根證書 B的二級CA B1來簽發的,但是根證書B並沒有在瀏覽器中內置,所以瀏覽器不會信任用戶證書D,這是因為瀏覽器在驗籤時只驗證到B1這一層然後找不到根證書B就無法驗證下去了。
如果二級CA B1證書是由根證書A來簽名,那這個問題就解了,所以新的根證書機構B會將二級CA證書(準確地講是二級CA的證書籤名請求文件)給老的根證書A來簽名,然後得到一個新的中間證書就是交叉證書。
瀏覽器在驗證的時侯用了新的信任鏈,這樣就可以解決用戶證書的信任問題。
當B的根證書全部部署完成後,再替換成自己的二級CA就可以了。
證書分類
證書分有三類:DV、OV、EV
DV證書只校驗域名的所有權,所以我們在申請DV證書的時候CA會提供幾種驗證域名所有權的方法:比如文件校驗、DNS校驗、郵件校驗,DV證書支持單域名、多域名和泛域名,但不支持多個泛域名,只支持一個泛域名,一般價格比較便宜,具體價格跟域名的數量有關,域名越多價格越高。
OV證書要驗證組織機構,在申請證書時CA會打電話確認這個域名是否真的屬於相應的公司或者機構,OV證書是單域名、多域名、泛域名和多個泛域名都支持,價格一般比DV證書要貴。
EV證書的校驗就更細了,比如組織機構的地址之類的,EV證書會在地址欄上顯示組織機構的名稱,EV證書只支持單域名或者多個域名,不支持泛域名,而且更貴,所以普通用戶一般不會用EV證書。
證書吊銷
證書是有生命週期的,如果證書的私鑰洩漏了那這個證書就得吊銷,一般有兩種吊銷方式:CRL和OCSP。
CRL是CA機構維護的一個已經被吊銷的證書序列號列表,瀏覽器需要定時更新這個列表,瀏覽器在驗證證書合法性的時候也會在證書吊銷列表中查詢是否已經被吊銷,如果被吊銷了那這個證書也是不可信的。可以看出,這個列表隨著被吊銷證書的增加而增加,列表會越來越大,瀏覽器還需要定時更新,實時性也比較差。
所以,後來就有了 OCSP 在線證書狀態協議,這個協議就是解決了 CRL 列表越來越大和實時性差的問題而生的。有了這個協議,瀏覽器就可以不用定期更新CRL了,在驗證證書的時候直接去CA服務器實時校驗一下證書有沒有被吊銷就可以,是解決了CRL的問題,但是每次都要去CA服務器上校驗也會很慢,在網絡環境較差的時候或者跨國訪問的時候,體驗就非常差了,OCSP雖然解決了CRL的問題但是性能卻很差。所以後來就有了OCSP stapling。
OCSP stapling主要解決瀏覽器OCSP查詢性能差的問題,本來由瀏覽器去CA的OCSP服務器查詢證書狀態的,現在改由域名的服務器去查詢,將OCSP的結果告訴瀏覽器即可,一般服務器的網絡性能要好於用戶電腦的網絡,所以OCSP stapling的性能是最好的。但是並不是所有的服務器都支持OCSP stapling,所以目前瀏覽器還是比較依賴CRL,證書吊銷的實時性要求也不是特別高,所以定期更新問題也不大。
HTTPS工作模式
對於客戶端和服務器來講,應用層協議還是HTTP,只是加了一個SSL層來做加密解密的邏輯,對應用層來說是透明的,在網絡上傳輸的都是加密的請求和加密的響應,從而達到安全傳輸的目的。
這是SSL層在網絡模型的位置,SSL屬於應用層協議。接管應用層的數據加解密,並通過網絡層發送給對方。
更細地分,SSL協議分握手協議和記錄協議,握手協議用來協商會話參數(比如會話密鑰、應用層協議等等),記錄協議主要用來傳輸應用層數據和握手協議消息數據,以及做加解密處理。我們應用層的的消息數據在SSL記錄協議會給分成很多段,然後再對這個片段進行加密,最後在加上記錄頭後就發送出去。
TLS握手協議
SSL/TLS 握手協議又細分為四個子協議,分別是握手協議、密碼規格變更協議、警告協議和應用數據協議。
完整的握手流程
首先是TCP握手,TCP三次完成之後才進入SSL握手,SSL握手總是以ClientHello消息開始,就跟TCP握手總是以SYN包開始一樣;
ClientHello主要包含客戶端支持的協議、密鑰套件、session id、客戶端隨機數、sni、應用層協議列表(http/1.1、h2)、簽名算法等等;
服務器收到ClientHello之後會從中選取本次通信的協議版本、密鑰套件、應用層協議、簽名算法,以及服務器隨機數,然後通過ServerHello消息響應,如果沒有session複用,那將服務器的證書通過Certificate消息響應,如果是選用了ECC算法來做密鑰交換的話,那還會將橢圓曲線參數以及簽名值通過ServerKeyExchange消息發送給對方,最後通過ServerHelloDone消息來結束本次協商過程;
客戶端收到這些消息之後,會生成預主密鑰、主密鑰和會話密鑰,然後將橢圓曲線參數通過ClientKeyExchange發送給服務器,服務器拿到客戶端的橢圓曲線參數也會生成預主密鑰,如果是RSA的話ClientKeyExchange用來發送公鑰加密的預主密鑰,服務器用私鑰來解密一下就可以得到預主密鑰,再由預主密鑰生成主密鑰和會話密鑰。最後雙方都以ChangeCipherSpce和Finished消息來告知對方,自己已經準備好了可以進行加密通信了,然後握手完成。
可以看出,完整的SSL握手需要2個RTT,而且每次握手都用到了非對稱加密算法簽名或者解密的操作,比較耗CPU,所以後來就有了session複用的優化手段,後面會做介紹。
SSL的握手消息雖然比較多,但很多消息都是放在一個TCP包中發送的,從抓包可以看出完整的SSL握手需要2個RTT。
剛才通過完整的SSL握手可以看出幾個缺點:
1、2個RTT,首包時間較長
2、每次都要做非對稱加密,比較耗CPU,影響性能
3、每次都要傳證書,證書一般都比較大,浪費帶寬
SSL握手的目的是協商會話密鑰以及參數,如果能把這些會話參數緩存那就可以沒有必要每次都傳證書和做非對稱加密計算,這樣就可以提高握手性能,而且可以將RTT減到1個,提高用戶體驗。
所以就有了session id的複用方式,每次完整握手之後都將協商好的會話參數緩存在服務器中,客戶端下次握手時會將上次握手的session id帶上,服務器通過session id查詢是否有會話緩存,有的話就直接複用,沒有的話就重新走完整握手流程。但是session id這種方式也有缺點,比較難支持分佈式緩存以及耗費服務器的內存。
而session ticket 方案,其原理可以理解為跟 http cookie 一樣,服務器將協商好的會話參數加密成 session ticket,然後發送給客戶端,客戶端下次握手時會將這個session ticket帶上,服務器解密成功就複用上次的會話參數,否則就重新走完整握手流程。可以看出session ticket這種方式不需要服務器緩存什麼,支持分佈式環境,有很大的優勢。這種方式有一個缺點是並不是所有客戶端都支持,支持率比較低,但隨著客戶端版本的更新迭代,以後各種客戶端會都支持。因為session id客戶端支持率比較高,所以目前這兩種方式都在使用。
這是 session ticket 的複用,跟 session id 的區別是 client hello 會帶上 Session ticket ,將近200個字節的加密數據,服務器會用session ticket 密鑰來解密,解密成功則直接複用,也簡化了握手流程,提升效率和性能。
這是我們上了分佈式 Session id 複用的效果,session id複用的比例在沒有開啟分佈式緩存時只佔了3%左右,複用率很低,上了分佈式session緩存之後,這個比例提升到20%多。握手時間也從75ms左右降低到65ms左右,性能提升效果還是很明顯的。
SSL/TLS握手時的私鑰用途(RSA、ECDHE)
我們知道私鑰是整個SSL中最重要的東西,那私鑰在SSL握手裡面又是怎麼使用的呢?兩種使用方式分別是:使用RSA來做密鑰交換和使用ECDHE來做密鑰交換。對於RSA來說,客戶端生成預主密鑰,然後用公鑰加密再發給服務器,服務器用私鑰來解密得到預主密鑰,然後由預主密鑰生成主密鑰,再由主密鑰生會話密鑰,最後用會話密鑰來通信。
對於ECDHE來說,客戶端和服務器雙方是交換橢圓曲線參數,私鑰只是用來簽名,這是為了保證這個消息是持有私鑰的人給我發的,而不是冒充的。雙方交換完參數之後生成預主密鑰,再生成主密鑰和會話密鑰。這就跟剛才RSA後面的流程一樣了。
可以看出RSA和橢圓曲線密鑰交換算法的私鑰用途是不一樣的,RSA密鑰交換時是用來做加解密的,橢圓曲線密鑰交換時是用來做簽名的。
SSL/TLS中的密鑰
預主密鑰、主密鑰和會話密鑰,這幾個密鑰都是有聯繫的。
對於RSA來說,預主密鑰是客戶端生成,加密之後發給服務器,服務器用私鑰來解密。對於ECDHE來說,預主密鑰是雙方通過橢圓曲線算法來生成。
主密鑰是由預主密鑰、客戶端隨機數和服務器隨機數通過PRF函數來生成;會話密鑰是由主密鑰、客戶端隨機數和服務器隨機數通過PRF函數來生成,會話密鑰裡面包含對稱加密密鑰、消息認證和CBC模式的初始化向量,但對於非CBC模式的加密算法來說,就沒有用到這個初始化向量。
session 緩存和session ticket裡面保存的是主密鑰,而不是會話密鑰,這是為了保證每次會話都是獨立的,這樣才安全,即使一個主密鑰洩漏了也不影響其他會話。
Keyless
剛才提到RSA和ECDHE的私鑰用途,對於服務器來說,密鑰交換算法是RSA時,私鑰是用來做解密的,ECDHE時,私鑰是用來簽名的。
Keyless就是將私鑰參與運算的部分分離遠程服務器,這樣可以解決兩個問題:
1,公私鑰分離,用戶不需要將私鑰給第三方,減少私鑰洩露的風險。
2,將非對稱加密運算這種cpu密集型運算剝離到keyserver,可以在keyserver中安裝ssl加速卡做硬件加速,提高性能。
HTTP/2
HTTP/2主要有這幾個特性:
一、二進制協議,可以做更多的優化;
二、多路複用,一定程度上解決了隊頭阻塞的問題,提高併發和總的加載時間
三、頭部壓縮,很多請求頭或者響應頭都沒有必要重複傳輸浪費帶寬,比如user-agent、cookie還有其他沒有必要每次都傳的頭部在http2中都做了優化
四、安全,雖然RFC規定HTTP/2可以運行在明文之上,但目前所有瀏覽器都只支持https之上的http/2,所以是安全的。
開啟HTTP/2會有這幾個收益:
一、加載時間的提升,我們做了這麼一個測試,一張大圖片分割成幾百個小圖片,然後用http/1.1和http/2打開,http/1.1加載完所有小圖片用了五六秒,但http/2加載完所有小圖片只需要不到1s的時間,有5倍左右的提升,效果還是很明顯的,具體提升百分之多少這得具體看具體的業務類型。
二、頭部壓縮有95%左右的提升,http/1.1的平均響應頭大小有500個字節左右,而http/2的平均響應頭大小隻有20多個字節,提升非常大,所以http/2非常適合小圖片小文件的業務類型,因為小文件的http頭部比重較大,而http/2對頭部的壓縮做了非常好的優化。
三、服務器QPS的性能有60%多的提升,這是因為http/2的連接複用和多路複用機制,可以處理更多的併發請求。
三、HTTPS實踐
本章節會重點給大家講講 Tengine 如何配置 https、HTTP/2、TLSv1.3,以及如何實現動態證書。
Tengine如何開啟HTTPS
http {
ssl_session_tickets on;
ssl_session_ticket_key ticket.key;
server {
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 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;
ssl_prefer_server_ciphers on;
ssl_certificate www.alicdn.com.crt;
ssl_certificate_key www.alicdn.com.key;
ssl_session_cache shared:SSL:256M;
ssl_session_timeout 15m;
#……
}
}
在Tengine中開啟http2,只需要在listen的後面加上http2參數就可以了。
但是有一個場景需要注意,因為有些域名並不想開啟http2,比如上面這個配置的b.com並不想開http2,但是因為a.com開啟了http2,所以b.com也被自動開啟了,這是因為http2這個參數作用在ip和端口上,在ssl握手時用了a.com的配置參數,所以tengine針對這種情況做了一個域名級別的開關來做控制。
TLSv1.3——更快、更安全
1、1-RTT & 0-RTT
2、只支持完全前向安全性的密鑰交換算法
3、ServerHello 之後的所有消息都是加密的
4、淘汰 Session ID 和 Session Ticket,用 PSK 代替
5、Chrome 63+, Firefox 58+
Tengine也支持了TLSv1.3,開啟方式:
server {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-AES-128-GCM-SHA256:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384: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;
#……
}
Tengine 實現與配置動態證書
動態證書主要解決的問題是接入域名太多,server塊過多導致tengine reload慢的問題,lua-nginx模塊提供了一個證書的lua階段,可以在這個階段來做證書的熱加載,不需要reload tengine,這樣可以提高效率和性能。
配置也比較簡單,在ssl_cert.lua裡面做證書的管理,在ssl握手時拿到sni,去拿這個域名的證書和私鑰,再調用lua ffi接口就可以完成證書和私鑰的切換。
ssl_cert.lua:
調用 lua ffi 接口設置證書和私鑰
四、HTTPS調試
我們知道HTTP是明文傳輸,調試就很簡單,抓包就可以看得清清楚楚,但HTTPS是加密的,抓包看到的是密文,這一節我告訴大家怎麼去解密HTTPS抓包文件。
抓包解密
方法一:
配置Wireshark:
Wireshark preferences… Protocols SSL (Pre)-Master-Secret log filename => /tmp/sslkey.txt
(一):
export SSLKEYLOGFILE=/tmp/sslkey.txt
(二):
openssl s_client -connect 127.0.0.1:443 -servername www.alicdn.com -keylogfile /tmp/sslkey.txt
(三):
echo “CLIENT_RANDOM 7EC0498BCF09E8300A1E9F8BA6C81E2A4383D7CDCFB10907B4074520FA8DF680 FA2457782F6FAECE47CF8E01BF9E0A441CEA8DCC91664F42F45F1EF5AB18ED902E35825713FF2D4D9651CE51ED885BB4
”>> /tmp/sslkey.txt
方法二:
強制使用RSA密鑰交換算法,Wireshark配置私鑰。
抓包解密-wireshark設置
常用命令及參數
curl
寫在最後
證書的購買和申請是非常複雜耗時的,為了縮短開通週期,為開發者提供最大化便利,簡化HTTPS加速設置環節,目前阿里雲CDN已經支持控制檯可實現一鍵開通HTTPS,後臺將完成代理免費證書購買、證書節點部署以及證書到期之前自動續簽,幫助開發者更便捷的完成全站HTTPS訪問加速。
閱讀更多 阿里云云棲號 的文章