TLS/SSL高級進階
TLS/SLL 是現(xiàn)在網(wǎng)絡(luò)安全通信比較重要的一環(huán),通過一些列的 key 交換和 key 生成,最終確立加密通道的整個流程。眾所周知,TLS/SSL 耗費的時間也是挺可觀的,相對于 TCP 的3次 RTT 來說,如果加上 TLS/SSL, 則總的 RTT 時間至少為 4 次。雖然看起來很多,但如果相對于現(xiàn)在的網(wǎng)絡(luò)環(huán)境來說,大概也就每次 20~30ms,這樣算下來,估計也就 100ms 左右。這樣的時間差還是可以忍受的,不過,這里還沒算入 DNS 解析,這個暫時不考慮。而且,TLS/SSL 生成的 sessionkey 如果在有效期內(nèi)的話,那么這個時間就可以完全忽略掉了。不多說了,我們直接來看下 TLS/SSL 的基本內(nèi)容。
TLS 算法
TLS/SSL 其實就是通過非對稱加密,生成對稱加密的 sessionkey 的過程。對稱加密的算法無外乎就 AES,或者使用 Cipher/Decipher 模塊。而非對稱加密常用的就是 RSA,不過也有使用 Diffie-Hellman (迪菲)。但,較安全性來說,DH 要高一些。因為,RSA 在生成 sessionkey 時,最后是由 browser 生成,然后通過 public key 加密后傳給 server 的,這樣存在一定的問題是,如果 hacker 得到了 private key,那么,他可以全程監(jiān)控流量,然后使用 private key 進行解密,那么可想而知,sessionkey 也就暴露了。
但對于 DH 來說,它機制的不同點在于,sessionkey 不會通過網(wǎng)絡(luò)傳輸,而是在兩端獨立生成的。ok~ 這就涉及到兩個 key 的一致性問題。DH 還有一個機制,即,前向安全性(perfect forward secrecy--PFS):server 端的 private key,不能用來代替以前任何一把的 sessionkey,所以,也無法破解以前任何一次的 session 內(nèi)容。每次連接,DH 都會重新生成一個 key,并且當該次 session 結(jié)束時,丟棄它。不過,這并不是很大的問題,因為 DH 能很快的生成 key。因為它耗費在網(wǎng)絡(luò)上的時間相比于 RSA 來說少了一半??梢詮南聢D簡單了解 DH 的加密算法:
簡單來說,兩邊通過一次的信息交換,完成了密鑰生成。因為 sessionkey 是獨立放在兩端的,為了達到一致性,每次連接時,DH 都需要重新協(xié)商生成 sessionkey?,F(xiàn)在有個問題是: 為什么一定要有 session key,他存在的意義是什么?
sessionkey 用途
TLS/SSL 其實就是通過非對稱加密,生成對稱加密的 session key 的過程
那 session key 主要又干了什么呢?首先,我們要先明確,session key 是用來進行對稱加密的,這種加密方式主要使用到的是 AES 加密算法。這不同于 Elliptic Curve Diffie-Hellman (ECDH) 這種非對稱加密算法。兩者其實都可以用來對信息進行加密,但由于算法內(nèi)部的實現(xiàn)機理不同,他們所用的時間也是不一樣的。基本上是,ECDH 所用的時間是 AES 的 3 倍。
你可以自行測試一下:
- openssl speed ecdh
- openssl speed aes
ok,上面大概簡述了 TLS/SSL 所用到的算法。接下來,我們來了解一下,具體 TLS/SSL 密鑰交換的過程。
TLS/SLL 過程
在詳述過程之前,我們需要了解一下,在過程中會出現(xiàn)的內(nèi)容。
- session key: 這是 TLS/SSL 最后協(xié)商的結(jié)果,用來進行對稱加密。
- client random: 是一個 32B 的序列值,每次連接來時,都會動態(tài)生成,即,每次連接生成的值都不會一樣。因為,他包含了 4B 的時間戳和 28B 的隨機數(shù)。
- server random: 和 client random 一樣,只是是由 server 端生成。
- premaster secret: 這是 48B 的 blob 數(shù)據(jù)。它能和 client & server random 通過 pseudorandom (PRF) 一起生成 session key。
- cipher suite: 用來定義 TLS 連接用到的算法。通常有 4 部分:
- 非對稱加密 (ECDH 或 RSA)
- 證書驗證 (證書的類型)
- 保密性 (對稱加密算法)
- 數(shù)據(jù)完整性 (產(chǎn)生 hash 的函數(shù)
比如 AES128-SHA 代表著:
*RSA 算法進行非對稱加密
*RSA 進行證書驗證
*128bit AES 對稱加密
*160bit SHA 數(shù)據(jù)加密算法
- 比如 ECDHE-ECDSA-AES256-GCM-SHA384 代表著
*ECDHE 算法進行非對稱加密
*ECDSA 進行證書驗證
*265bit AES 對稱加密
*384bit SHA 數(shù)據(jù)加密算法
相信大家對這張圖已經(jīng)很熟悉了:
不過,上面不是說了有兩種不同的非對稱加密方式嗎? RSA & DH? 那為什么圖只是一個嘞?
ok,實際上這圖沒錯,他并沒有把傳輸?shù)膬?nèi)容是什么寫出來,這很關(guān)鍵。而兩種算法也是在傳輸內(nèi)容上區(qū)分開的,基本的過程是完全一樣的。根據(jù) wiki 的解釋,我們大概能知道整個傳輸過程需要的內(nèi)容。
- 客戶端發(fā)送 clientHello 信息,包含了客戶端支持的最高 TLS 協(xié)議版本,random num (上文提到過),cipher suite。如果,客戶端使用 resumed handshake,那么這里發(fā)送的就是 sessionID。如果,客戶端還支持 ALPN,那么它應(yīng)該還需要發(fā)送它所支持的其他協(xié)議,比如 HTTP/2.
- 在 server 端進行 serverhello 階段,這里 server 根據(jù) client 發(fā)送過來的相關(guān)信息,采取不同的策略,同樣會發(fā)送和 client 端匹配的 TLS 最高版本信息,cipher suite 和 自己產(chǎn)生的 random num. 并且,這里會產(chǎn)生該次連接獨一無二的 sessionID。
- 通過 certificate 階段,會在信息流中加入 public key certification。ServerKeyExchange 該階段,主要是針對于 ECDH 加密方式,這里就不贅述,后面再進行講解。
- serverHelloDone 標識這 server 階段處理結(jié)束,將該階段產(chǎn)生的信息發(fā)送給 client。
- 在 clientKeyExchange 階段時,client 會隨機生成一串 pre-master secret 序列,并且會經(jīng)由 public key 加密,然后發(fā)送給 server。在 ChangeCipherSpec 階段,主要是 client 自己,通過 pre-master secret + server random-num + client random-num 生成 sessionKey。這就標識著,此時在 client 端,TLS/SSL 過程已經(jīng)接近尾聲。
- 后面在 server 端進行的 ChangeCipherSpec 和 client 進行的差不多,通過使用 private key 解密 client 傳過來的 pre-master secret ,然后生成 sessionkey。 后面再通過一次驗證,使用 session Key 加密 server finshed,發(fā)送給 client,觀察能夠成功解密,來表示最終的 TLS/SSL 完成。
上面主要是根據(jù) RSA 加密方式來講解的。因為 RSA 才會在 TLS/SSL 過程中,將 pre-master secret 顯示的進行傳輸,這樣的結(jié)果有可能造成,hacker 拿到了 private key 那么他也可以生成一模一樣的 sessionKey。即,該次連接的安全性就沒了。
接下來,我們主要講解一下另外一種加密方式 DH。它和 RSA 的主要區(qū)別就是,到底傳不傳 pre-master secret。RSA 傳而 DH 不傳。
根據(jù) cloudflare 的講解可以清楚的了解到兩者的區(qū)別:
這是 RSA 的傳輸方式,基本過程如上述。
而 DH 具體區(qū)別在下圖:
這里先補充一下 DH 算法的知識。因為,pre-master secret 就是根據(jù)這個生成的。DH 基本過程也不算太難,詳情可以參考 wiki。 它主要運用到的公式就是:
為了防止在 DH 參數(shù)交換時,數(shù)據(jù)過大,DH 使用的是取模數(shù)的方式,這樣就能限制傳輸?shù)闹涤肋h在 [1,p-1]。這里,先說明一下 DH 算法的基本條件:
- 公共條件: p 和 g 都是已知,并且公開。即,第三方也可以隨便獲取到。
- 私有條件: a 和 b 是兩端自己生成的,第三方獲取不到。
基本流程就是:
我們只要把上圖的 DH parameter 替換為相對應(yīng)的 X/Y 即可。而最后的 Z 就是我們想要的 Premaster secret。 之后,就和 RSA 加密算法一致,加上兩邊的 random-num 生成 sessionKey。通過,我們常常稱 DH 也叫作 Ephemeral Diffie-Hellman handshake。 因為,他每次一的 sessionKey 都是不同的。
而 RSA 和 DH 兩者之間的具體的區(qū)別就在于:RSA 會將 premaster secret 顯示的傳輸,這樣有可能會造成私鑰泄露引起的安全問題。而 DH 不會將 premaster secret 顯示的傳輸。
TLS/SSL 中的基本概念
上面內(nèi)容大概講清楚了基本的 TLS/SSL 的加密過程。不過,其中,還有很多其他的小細節(jié),比如 SNI,ALPN,F(xiàn)orward Secrey。 接下來,我們主要將這些細節(jié)將一下,因為他們其實也很重要。
Forward Secrey
FS(Forward Secrey) 主要是針對 private key 進行描述了。如果你的 private key 能夠用來破解以前通信的 session 內(nèi)容,比如,通過 private key 破解你的 premaster secret ,得到了 sessionKey,就可以解密傳輸內(nèi)容了。這種情況就是 non-forward-secrey。那如何做到 FS 呢? 很簡單,上文也已經(jīng)提到過了,使用 DH 加密方式即可。因為,最后生成的 sessionKey 和 private key 并沒有直接關(guān)系,premaster secret 是通過 g(ab) mod P 得到的。
簡單的說就是,如果你想要啟用 FS,那么你應(yīng)該使用的是 DH 加密方式,而放棄 RSA。不過,由于歷史原因(TLS 版本問題),RSA 現(xiàn)在還算是主流的加密方式。但,DH 也憑借他 5S 的安全性,份額也在增加。
ALPN
ALPN 全稱是 Application Layer Protocol Negotiation(應(yīng)用層協(xié)議協(xié)商機制)??吹綉?yīng)用層,程序員們應(yīng)該都能反應(yīng)出 OSI 7層網(wǎng)絡(luò)協(xié)議。在應(yīng)用層中,HTTP 協(xié)議應(yīng)該是重點。不過,由于 HTTP 版本的問題,以及現(xiàn)在 HTTP2 的流行,為了讓 client-server 使用相同的協(xié)議而出現(xiàn)了 ALPN。ALPN 實際上是從 SPDY 中的 NPN 協(xié)議衍生出來的。不過,它們倆的機制正好相反。
- NPN: 由 server 端告訴 client,它支持什么協(xié)議,然后 client 確認支持的協(xié)議后,開始進行連接。
- ALPN:在 TLS 階段,由 client 告訴 server,它所支持的所有協(xié)議,然后開始進行連接。
總的來說,NPN 已經(jīng)退出歷史的舞臺了。。。ALPN 現(xiàn)在是 IETF 指定的標準協(xié)議。ALPN 在 TLS 具體的過程是:
- 在 clientHello 階段,client 會在 message 中,添加一個 ProtocolNameList 字段。用來表示它所支持的協(xié)議列表
- server 端在 serverHello 階段,處理 client 提供的 ProtocolNameList。并且選擇最高版本的協(xié)議,執(zhí)行。將選擇信息添加到 serverhello 內(nèi)。
SNI
SNI 的全稱為:Server Name Indication。該機制的提出的意義是,當有一個 server 同時處理了很多個 host 時。相當于,一個 IP 映射多個域名,但,由于證書只能對一個 3 級域名有效,所以,針對于多個 host 來說,server 為了能同時兼顧這些域名。一種簡單的辦法就是重定向到指定域名,如果都想支持的話,也行,掏錢自己多買幾個證書 (真土豪)。如果,你很土豪的話,現(xiàn)在就有這樣的情況,一個 IP 服務(wù)器下,搭載了支持多個域名的 server,并且每個域名都有合法的 CA 證書。那么,server 怎么判斷,哪一個域名用哪一個證書呢?這時候,就需要用到 SNI。相當于在 TLS 階段,將 host 一并發(fā)送過去,然后 server 就知道在 serverhello 階段該返回啥證書了。
現(xiàn)在,有個問題,為什么一定要用 SNI 呢?
我們回想一下,這里我們僅僅只是建立 TCP + TLS 連接,客戶端的一些內(nèi)容,比如 hostname,我們并不能在 TCP 中獲得。而,想要獲得的話,就需要等到 HTTP 階段,獲得 client 傳過來的 host 或 origin 字段。所以,為了解決這個比較尷尬的點,就提出了 SNI。
Session Resumption
感覺能看到這里的人,應(yīng)該都是閑的蛋疼的人。。。如果讓我來看這篇文章,估計看幾張圖,我基本上就直接關(guān)網(wǎng)站了。因為,這實在是復(fù)雜。并且,上面說的只是協(xié)議上的復(fù)雜性,對于計算機來說,只需要記下每一次該發(fā)什么東西而已,但真正讓 Computer 感到蛋疼的是,key 的計算。特別是 random key 和 premaster secret 動不動就是 32B,48B 的數(shù)據(jù)量。所以,為了真正減少計算機的工作量(實際上是 server),提出了 Session ID 和 Session Tickets,來將成功進行連接的 session 內(nèi)容緩存起來。
Session ID
Session ID 是 server 將上一次成功連接的 session 內(nèi)容存在自己的硬盤里面。這里,就不涉及對 session data 的二次加密?;镜倪^程是:
- client 端在 clientHello 階段,將 random num,TLS protocol 和通過 hostname 匹配到的最新一次的 session ID 發(fā)送給 server 端。(也就是說,client 同樣需要存儲一份 session data)
- server 接收到 session ID 后,在緩存中查找,如果找到,則直接進行 ChangeCipher 階段,開始生成sessionKey。然后,返回相同的 sessionID 即可。
那么相對于完全的 TLS/SSL 連接來說,這里只用到了一次 RTT。那么協(xié)議過程就變?yōu)榱耍?/p>
Session Ticket
既然 Session ID 是為了解決網(wǎng)絡(luò)時延和計算機性能問題,那么 Session Ticket 又干了什么呢?
Session Ticket 和 Session ID 做的也是同樣的事情,server 將最新一次的 sesion data 通過二次加密,在上一次握手結(jié)束時傳遞過去,然后 client 將傳遞過來的信息保存。
這樣,利不利用緩存的 session data 這時,就取決于 client。如果該次的 session data 沒有過期,那么 client 就會在 clientHello 階段將該數(shù)據(jù)發(fā)送過去,server 接受到之后,便開始進行解密然后,雙方生成 sessionKey,握手結(jié)束。
那 Session Ticket 和 Session ID 到底用哪一個呢?
這估計得看你的業(yè)務(wù)情況了,Session ID 注重的是節(jié)省性能,而損耗部分空間。Session Ticket 注重的是節(jié)省空間,而損耗部分性能。它們兩者都能節(jié)省一次 RTT 時間,用誰,還是得看你的服務(wù)器的具體情況。
CA 證書詳情
前面大致說了 TLS/SSL 是怎樣運作的,以及有哪些連接方法。相當于,學(xué)畫一條線一樣,我們現(xiàn)在只知道這條線該畫多長,但還不知道,這條線從哪里畫。所以,接下來,我們就需要來探討一下,兩端發(fā)生了什么。其實也不難,主要還是關(guān)于 CA 證書的存放和驗證。server 端的很簡單,就是把自己的 CA 證書發(fā)過來就 ok。但,client 驗證這個證書是否可信,會有點復(fù)雜。
首先,證書頒發(fā)機構(gòu)就那么一些,換句話理解就是,每個證書頒發(fā)機構(gòu),就代表著一張 CA 證書。但,現(xiàn)在市面上的 HTTPS 網(wǎng)站,辣么辣么多,難道他們都用同一張證書?難道他們都有一樣的 pu/pr key? 那么 HTTPS 安全還有用嗎?
所以,按照上面的推理,我們的網(wǎng)站上的 HTTPS 證書,肯定都是各不一樣的。一般來說,有 3 種類型的證書: DV(Domain Validation),OV(Organization Validation),EV(Extended Validation)。均價按照順序上升,所以,最便宜的就是 DV,這應(yīng)該是我們勤勞的貧苦大眾用得起的。它們之間具體的區(qū)別在于域名的支持上:
- DV:就是個人證書嘛,基本支持的就是單域名和多域名,不支持泛域名(*.villainhr.com)。不過,看價格,比如我這個就是騰訊云給的一個免費的 DV 證書,所以,就支持一個 3 級域名(https://www.villainhr.com)。如果是收費的,單/多域名應(yīng)該都支持。
- OV:就比較牛逼,面向企業(yè)的,多域名/泛域名都支持。
- EV:屬于貴族用的,一般人也搞不到,主要它還需要去買個保險。。。
那我們的證書在蕓蕓證書中,是處于哪一個層級呢?一般是三級。怎么體現(xiàn)的呢?
那這么多證書,我們用的是哪一個呢?當然是,最下面那個。因為每個證書并不是都被信任,所以客戶端首先就要了解一下,你這個證書能否用來進行驗證。如果不行的話,那么你這次連接就是不被信任的,就沒有綠色的小鎖。這就需要了解一下,客戶端的驗證過程。
CA 鏈式驗證
首先,什么叫做可信的證書呢?
我們先要明白一個道理,HTTPS 是先建立在人與人之間的相互信任上,然后才建立在機器與機器的相互信任上。假如,根證書 A 機構(gòu),惡意的將一個以前頒發(fā)過的證書,又給了另外一個不要臉攔截站點(比如,用來插廣告的)。這樣,我拿到了這個證書后,就可以自己搭一個服務(wù)器,用來進行攔截瀏覽,監(jiān)管里你網(wǎng)站,并強行插廣告。這就被稱為不可信的機構(gòu)/證書。
而驗證的可行性,通常又跟機構(gòu)的權(quán)威性有著極大的關(guān)系。它基本的驗證過程簡述就是(按照上面的層級):
- www.villainhr.com 問 TrustAsia DV SSL,我的證書可不可信?
- 可信!ok,繼續(xù)。TrustAsia DV SSL 問 VeriSign Class 3,我的證書可不可信
- 可信!ok,然后便開始 TLS/SSL 連接。
如果上述任一步驟出現(xiàn)問題,那么該次 TLS/SSL 就不會進行,會回退。那么它們在詢問的時候,會不會發(fā)送網(wǎng)絡(luò)請求呢?不會~ 因為,電腦在初始化時,會自帶很多可信任的證書機構(gòu)(即,Root CA),也就是我們剛剛提到的 VeriSign Class 3 的證書機構(gòu)。以及,能夠簽發(fā)證書的二級機構(gòu)(比較少)。到時候,瀏覽器會自動的根據(jù)數(shù)字簽名來進行證書的驗證。
CA 合法驗證
上面已經(jīng)闡述了,CA 證書的合法性是自下而上的驗證方式。那么它們具體驗證協(xié)議是怎樣的呢?在說之前,我們先說幾個概念:
- 數(shù)字簽名:它是用頒發(fā)機構(gòu)的私鑰,對下級證書的公鑰進行加密生成的值。digital_sign = CA_pr_key + sub_Cer_ppu_key。
- 解密:用頒發(fā)機構(gòu)的公鑰對數(shù)字簽名進行解密,對比下級證書的公鑰和解密后的值是否一致。
CA 驗證首先需要說一下它的頒發(fā)過程:
- 頒發(fā)機構(gòu) A,用自己的私鑰將需要生成的下級證書 B 的公鑰進行加密,生成數(shù)字簽名,然后再帶上相關(guān)信息:公鑰,公鑰的指紋,數(shù)字簽名,證書名,簽發(fā)機構(gòu)等。
然后,驗證過程就是根據(jù)這個來的:
- 瀏覽器解析下級證書 B 的相關(guān)信息,找到簽發(fā)機構(gòu)和數(shù)字簽名。
- 然后,找到簽發(fā)機構(gòu) A,使用 A 的公鑰去解密數(shù)字簽名,然后對比下級證書 B 的公鑰。如果成功則合法,反之,不合法。
而上面的三級證書層級,也是同樣的道理,自上而下的找就 ok。當然,有時候為了驗證的速度,會做一些緩存,這樣就不必再進行驗證了。所以,根據(jù)上面的描述,有童鞋可能會想到,能不能自簽證書呢?反正,瀏覽器也是從本地找的。
當然可以,openssl 就可以生成你自己的 CA。不過需要注意的是,你生成的 CA 只是在你自己的電腦上使用,如果你想保證你的 CA 在其他電腦上也能使用的話(這是不可能的),那就用錢砸就 ok。
具體的過程可以參考:生成自己的 CA 證書。
以前,在使用 Charles 和 Fiddler 的時候,一直在想,它們是怎么做到,將自己的證書,變成簽發(fā)機構(gòu)證書。
后來發(fā)現(xiàn),它是把證書中的相關(guān)字段該成它的證書內(nèi)容。不過,對于某些高級證書,還是會有一些問題,比如,wx.qq.com(微信的)
另外,為了證書的可靠性,提出了 Certificate Transparency 項目,實際上,就是讓證書機構(gòu)公開它的簽發(fā)流水。防止出現(xiàn)重復(fù)簽發(fā)。
證書的吊銷
現(xiàn)在有個問題,為什么證書有過期時間呢?
這同樣是為了安全性,前面說過,如果你的證書發(fā)生了泄漏(實際上就是私鑰)。那么,其他服務(wù)器就可以作為一個代理去攔截你的流量。這時候,由于過期的原因,可能一段時間后,中間惡意的服務(wù)器就沒用了,另外,如果你發(fā)現(xiàn)了你遺失了證書,可以向頒發(fā)機構(gòu)去掛失。
另外,還有一個原因是證書吊銷的 CRL 機制。簡單來說,就是有一個列表來記錄當前時間,該頒發(fā)機構(gòu)被吊銷的證書 list。如果,沒有過期時間的話,那么這個 list,會隨時間程指數(shù)增長,引入過期機制的話,該 list 只要記錄當前沒過期但吊銷的證書信息即可。
證書的吊銷有兩種機制:CRL,OCSP
CRL
CRL(Certificate Revocation List),即,證書吊銷列表。CA 機構(gòu)會生成一個列表,列表里面是當前周期被吊銷證書的序列號,當進行證書驗證時,同樣也會進行驗證該項。如果,已經(jīng)是吊銷證書的話,那么該次 TLS/SSL 連接也會失敗。我們可以從證書信息中找到 CRL URI:
該協(xié)議雖然簡單,但,缺陷還是比較多的。
下載時間。因為該 list 不是自帶的,需要從頒發(fā)機構(gòu)下載,這就造成了網(wǎng)絡(luò)時延。
緩存時間。如果存在緩存,就存在了信息不同步的問題,如果一個證書已經(jīng)過期,但緩存中顯示的是未過期,那么也是一個安全問題。
OCSP
OCSP(Online Certificate Status Protocol),即,在線證書狀態(tài)協(xié)議。它通過在線請求的方式來進行驗證,不需要下載整個 list,只需要將該證書的序列號發(fā)送給 CA 進行驗證。當然,驗證通過也會有一定的緩存期。不過,由于驗證也會存在時延。另外,部署 OCSP 對 CA 也有一定的要求,CA 要搭建的一個服務(wù)器來接受驗證,并且,該服務(wù)器的性能要好(負載很大)。
OCSP stapling
OCSP stapling 常稱為: TLS Certificate Status Request extension。是 OCSP 的另外一種實現(xiàn)方式,因為前兩個(OCSP,CRL)都是由客戶端去驗證證書是否吊銷,并且都會發(fā)送請求。而 OCSPs(OCSP stapling)則是直接在 server 端,進行證書的有效性驗證。server 會周期性的向 CA 機構(gòu)發(fā)送請求,驗證有效性,并在 certificate 階段,發(fā)送相應(yīng)的簽名信息。不過,該協(xié)議是建立在,我們完全信任 serve 的情況下,這里就排除了一些惡意的中間服務(wù)器。詳情可以參考:OCSP stapling。
TLS/SSL 優(yōu)化
TLS/SSL 主要的性能調(diào)優(yōu)簡單包括:啟用 False Start, OSCP Stapling, 選擇合適 cipher suite, resumption 等。另外,如果你追求fashion, 那么 HTTP/2 應(yīng)該是個不錯的選擇。
想要做 TLS/SSL 優(yōu)化,那么你必須了解,TLS/SSL 握手的整個過程是什么。當然,你可以買個證書,從頭自己搭建一個服務(wù)器,但是,這樣只能證明 你很有錢 外,其它也證明不了什么。因為,這完全可以自己內(nèi)網(wǎng)搭一個呀~ 可以參考:10s 自建證書. 這里,我們結(jié)合 nginx 來具體對 TLS/SSL handshake 優(yōu)化,做個整體的闡述。
設(shè)置 session 緩存
session 緩存設(shè)置可以讓兩次的 RTT,變?yōu)橐淮?,這相當于快了一倍(不包括,密鑰計算等)。不同的 server 設(shè)置 session 的辦法有很多,這里以 nginx 為例。在 nginx 中,支持的是 Session ID 的形式,即在 server 中緩存以前 session 的加密內(nèi)容。涉及的字段有兩個,ssl_session_cache 和 ssl_session_timeout。
- ssl_session_cache:用來設(shè)置 session cache 上限值,以及是否在多個 worker 之間共享
- ssl_session_timeout:用來設(shè)置 session cache 存儲的時間
看個 demo 吧:
- ssl_session_cache shared:SSL:10m;
- ssl_session_timeout 20m;
表示的意思是:session cahce 會在不同的 worker 之間分享,假設(shè) 1MB 只能存儲 8000 次握手的信息。那么, 10 MB 一共可以存儲 80000 次握手信息。如果超出,則不會存儲。緩存信息存在時長為 20分鐘。
另外,你也可以開啟 session ticket。ST(session ticket) 需要一個sign 參數(shù),使用 openssl 創(chuàng)建即可。
- $ openssl rand 48 > ticket.key
- # 在 nginx 中開啟
- ssl_session_tickets on;
- ssl_session_ticket_key ticket.key;
選擇合適的 cipher suite
這里先聲明一下,你的證書的內(nèi)容和你的加密套件實際沒有半毛錢關(guān)系,這主要還是取決于你的服務(wù)器的支持程度以及客戶端的支持度。另外,如果你想啟用 False Start,這也可套件的選擇有很大的關(guān)系。我們來看一下如果設(shè)置吧。在 nginx 中,主要用到兩個指令:
- ssl_prefer_server_ciphers on;
- ssl_ciphers xxx;
- ssl_prefer_server_ciphers: 用來告訴客戶端,要按照我提供的加密套件選擇。
- ssl_ciphers: 具體設(shè)置的加密套件內(nèi)容,使用 : 分隔。
支持性最高的就是使用:
- // 讓瀏覽器來決定使用哪一個套件(額。。。最后的手段)
- ssl_ciphers HIGH:!aNULL:!MD5;
一般情況,還是應(yīng)該自己來決定使用哪一個套件,這樣安不安全由自己說了算。具體可以參考 mozilla 的套件配置。這里簡單放一個,比較安全的,下面所有的套件都必須支持 Forwar Secrecy
- ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5
不過,以下的加密套件,最好不要使用,因為基本上都不安全:
- aNULL: 是一種非標準的 DH 密鑰交換套件。容易被中間人攻擊。
- eNULL: 沒有加密方式,明文交換
- EXPORT: 一種弱加密方式,老美那邊早期使用的
- RC4: 使用已經(jīng)廢棄的 ARCFOUR 算法
- DES: 使用已經(jīng)廢棄的 Data Encryption 標準
- SSLv2: 老版本 SSL2.0 的加密套件(最少,你也寫 SSLv3 嘛)
- MD5: 直接使用 MD5 加密方式
上面那些只能給一些遠古瀏覽器使用,基本上在選擇中是作為墊底的選擇。
False Start
另外,怎么在 nginx 中開啟 False Start 呢? 這其實和服務(wù)器并沒有多大的關(guān)系,關(guān)鍵還是你選擇的套件和 NPN/ALPN 協(xié)議的作用。
- 首先,你的加密套件必須具有 Forward Secrecy,否則開不了。
- 瀏覽器需要使用 NPN 或者 ALPN 告訴服務(wù)器,該所需的協(xié)議版本,然后再決定開不開啟。
那么,在 nginx 中,我們只要選擇好合適的加密套件即可。這里就放一份現(xiàn)成的吧
- ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256::DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5';
使用 DH 密鑰交換
DH 的加密過程,上面已經(jīng)說過了。DH 自帶兩個公共的參數(shù),所以,這必須手動進行創(chuàng)建(實際上就是將參數(shù) sign 一遍)。
- // 創(chuàng)建一個 DH param
- openssl dhparam 2048 -out dhparam.pem
然后,調(diào)用該文件
- ssl_dhparam dhparam.pem;
這樣,你就正式的開啟的 DH 加密模式。如果你使用抓包工具觀察一下,此時 DH 應(yīng)該會在 Server Hello 里:
不過,由于歷史原因,DH param 已經(jīng)使用的長度是 1024,比如: 采用 Oakley group 2 版本。現(xiàn)在,比較流行的 DH 加密方式是ECDHE,它和以前的加密方式(DHE)比起來,在密鑰生成這塊會快很多。同樣,由于歷史原因,它的基本條件比較高:(其實也還好)
- Android > 3.0.0
- Java > 7
- OpenSSL > 1.0.0
開啟 OCSP Stapling
OCSP Stapling 是驗證證書權(quán)威性的一種手段,前面還有兩種 CRL 和 OCSP。不過,它們都是讓 client 自己去驗證。而 OCSP Stapling 則把驗證這塊放到了 server 里,通過定期檢查,來減少網(wǎng)絡(luò)時間中的消耗。要開啟 OCSP Stapling 首先是需要你證書的 chain 文件,該是用來詳細說明,從根證書到你的證書中間所要經(jīng)歷的所有驗證(和其他兩種驗證手段一樣)。那如何得到 chain 文件呢?直接去問你的證書頒發(fā)機構(gòu),這個又不是啥秘密文件。如果是自發(fā)證書(自己測試用的),那就自己生成。將所有的中間證書按照 bottom to up 放到一個文件里:
- cat intermediate/certs/intermediate.cert.pem \
- certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem;
那么 ca-chain.cert.pem 就是 OSCP stapling 驗證文件。然后在 nginx 開啟即可。
- ssl_stapling on;
- ssl_stapling_verify on;
- ssl_trusted_certificate ca-chain.cert.pem;
- resolver 8.8.8.8 8.8.4.4; // 默認使用 Google 的
關(guān)于 DNS 解析,同樣你也需要問一下證書提供商,當然,該值可以不用管。下面也同樣適用
- ssl_stapling on;
- ssl_stapling_verify on;
- ssl_trusted_certificate ca-chain.cert.pem;
開啟過后,你可以使用 openssl s_client -connect www.yourDomainName.com:443 來測試一下,檢測是否開啟成功。
開啟 HSTS
HSTS(HTTP Strict Transport Security) 實際上就是一個響應(yīng)頭,沒啥很特別的,具體內(nèi)容就是,你所有對外部的請求都是https,所以這有一個問題,如果你的圖片地址是 http 的,那么最終的結(jié)果,會變?yōu)? https://xxx,有可能會造成資源丟失的情況,所以,開不開啟還需要慎用。
- Strict-Transport-Security: max-age=15768000 # 設(shè)定 6 個月的強制期
在有效時間內(nèi),客戶端都會嘗試使用 https 訪問你的站點,如果在這期限里你的證書過期了,開不了 https。那么,呵呵。
使用 SNI
SNI 就是針對一個 IP 手握很多張證書時,用到的協(xié)議機制,這主要是用來區(qū)分,不同的 host,使用不同的證書。SNI 詳情上面已經(jīng)說過了,這里就不贅述了。主要使用格式就是不同的 server_name 搭配不同的 certificate
- server{
- server_name www.abc.com;
- ssl_certificate abc.crt;
- ssl_certificate_key abc.crt.key;
- }
- server{
- server_name www.def.com;
- ssl_certificate def.crt;
- ssl_certificate_key def.crt.key;
- }
如何開啟呢?換個高版本的 nginx 就行了。你可以使用 nginx -V 檢查你的 nginx 是否帶有
- TLS SNI support enabled
完整示例
最后,放一份完整的吧:
- server {
- listen 443 ssl http2; # 默認打開 http2
- listen [::]:443 ssl http2;
- ssl_certificate /etc/nginx/cert/bjornjohansen.no.certchain.crt;
- ssl_certificate_key /etc/nginx/cert/bjornjohansen.no.key;
- ssl_session_cache shared:SSL:10m;
- ssl_session_timeout 20m;
- ssl_prefer_server_ciphers on;
- ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
- ssl_dhparam /etc/nginx/cert/dhparam.pem;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_stapling on;
- ssl_stapling_verify on;
- ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;
- resolver 8.8.8.8 8.8.4.4; # 看情況選擇 DNS IP
- #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
- # 我一般不開 HSTS
- # add_header Strict-Transport-Security "max-age=31536000" always;
- }