你想了解的 HTTPS 都在這裡

HTTP 協議僅僅制定了互聯網傳輸的標準,簡化了直接使用 TCP 協議進行通信的難度。有關 HTTP 協議相關的講解請看前面兩節:

HTTP 協議詳解

HTTP協議詳解(二)

less is more 的概念本身很好,但是過於簡單也會承擔一些後果:

通信使用明文,內容可能會竊聽

HTTP本身不具備加密的功能,所以也無法做到對通信整體(使用HTTP協議通信的請求和響應的內容)進行加密。即,HTTP報文使用明文(指未經過加密的報文)方式發送。

報文是否是正經用戶發出的報文無法得知,可能被篡改

HTTP協議無法證明通信的報文完整性,因此,在請求或響應送出之後直到對方接收之前的這段時間內,即使請求或響應的內容遭到篡改,也沒有辦法獲悉。
換句話說,沒有任何辦法確認,發出的請求/響應和接收到的請求/響應是前後相同的。

不驗證發送方身份,可能遭遇偽裝

HTTP協議中的請求和響應不會對通信方進行確認。在HTTP協議通信時,由於不存在確認通信方的處理步驟,任何人都可以發起請求。另外,服務器只要接收到請求,不管對方是誰都會返回一個響應(但也僅限於發送端的IP地址和端口號沒有被Web服務器設定限制訪問的前提下)

HTTP協議無法驗證通信方身份,任何人都可以偽造虛假服務器欺騙用戶,實現釣魚欺詐,用戶無法察覺。

因為有以上問題且隨着時代發展,黑客的技術能力也越來越強,非加密的 HTTP 請求很容易引起相關的網絡安全問題,對於安全性能的攻防控制需求也越來越強烈。

1. 什麼是安全

既然 HTTP 不安全,那什麼樣的通信過程才是安全的呢?

通常認為,如果通信過程具備了四個特性,就可以認為是 安全 的,這四個特性是:機密性、完整性,身份認證和不可否認。

  • 機密性(Secrecy/Confidentiality):是指對數據的保密,只能由可信的人訪問,對其他人是不可見的秘密,簡單來說就是不能讓不相關的人看到不該看的東西。

  • 完整性(Integrity,也叫一致性):是指數據在傳輸過程中沒有被竄改,不多也不少,完完整整地保持着原狀。

  • 身份認證(Authentication):是指確認對方的真實身份,也就是 證明你真的是你,保證消息只能發送給可信的人。

    如果通信時另一方是假冒的網站,那麼數據再保密也沒有用,黑客完全可以使用冒充的身份 套 出各種信息,加密和沒加密一樣。

  • 不可否認(Non-repudiation/Undeniable),也叫不可抵賴,意思是不能否認已經發生過的行為,不能說話不算數,耍賴皮

使用前三個特性,可以解決安全通信的大部分問題,但如果缺了不可否認,那通信的事務真實性就得不到保證,有可能出現 老賴

比如,小明借了小紅一千元,沒寫借條,第二天矢口否認,小紅也確實拿不出借錢的證據,只能認倒霉。另一種情況是小明借錢後還了小紅,但沒寫收條,小紅於是不承認小明還錢的事,說根本沒還,要小明再掏出一千元。

所以,只有同時具備了機密性、完整性、身份認證、不可否認這四個特性,通信雙方的利益才能有保障,才能算得上是真正的安全。

2. HTTPS 解決什麼問題

HTTPS 為 HTTP 增加了剛才所說的四大安全特性。

HTTPS 其實是一個 非常簡單 的協議,RFC 文檔只有短短的 7 頁,裏面規定了新的協議名https,默認端口號 443,至於其他的什麼請求 – 應答模式、報文結構、請求方法、URI、頭字段、連接管理等等都完全沿用 HTTP,沒有任何新的東西。

也就是說,除了協議名 http 和端口號 80 這兩點不同,HTTPS 協議在語法、語義上和 HTTP 完全一樣,優缺點也照單全收(當然要除去 明文不安全 )。

HTTPS 憑什麼就能做到機密性、完整性這些安全特性呢?

秘密就在於 HTTPS 名字里的 S,它把 HTTP 下層的傳輸協議由 TCP/IP 換成了 SSL/TLS,由 HTTP over TCP/IP 變成了 HTTP over SSL/TLS,讓 HTTP 運行在了安全的 SSL/TLS 協議上,收發報文不再使用 Socket API,而是調用專門的安全接口。HTTPS 本身並沒有什麼驚世駭俗的本事,全是靠着後面的 SSL/TLS 撐腰。只要學會了 SSL/TLS,HTTPS 自然就手到擒來。

HTTPS 協議的主要功能基本都依賴於 TLS/SSL 協議,TLS/SSL 的功能實現主要依賴於三類基本算法:散列函數 、對稱加密和非對稱加密,利用非對稱加密實現身份認證和密鑰協商,對稱加密算法採用協商的密鑰對數據加密,基於散列函數驗證信息的完整性。

15

3. 什麼是 SSL/TLS

現在我們就來看看 SSL/TLS,它到底是個什麼來歷。

SSL 即安全套接層(Secure Sockets Layer),在 OSI 模型中處於第 5 層(會話層),由網景公司於 1994 年發明,有 v2 和 v3 兩個版本,而 v1 因為有嚴重的缺陷從未公開過。

14

SSL 發展到 v3 時已經證明了它自身是一個非常好的安全通信協議,於是互聯網工程組 IETF 在 1999 年把它改名為 TLS(傳輸層安全,Transport Layer Security),正式標準化,版本號從 1.0 重新算起,所以 TLS1.0 實際上就是 SSLv3.1。

到今天 TLS 已經發展出了三個版本,分別是 2006 年的 1.1、2008 年的 1.2 和去年(2018)的 1.3,每個新版本都緊跟密碼學的發展和互聯網的現狀,持續強化安全和性能,已經成為了信息安全領域中的權威標準。

目前應用的最廣泛的 TLS 是 1.2,而之前的協議(TLS1.1/1.0、SSLv3/v2)都已經被認為是不安全的,各大瀏覽器即將在 2020 年左右停止支持,所以接下來的講解都針對的是 TLS1.2。

TLS 由記錄協議、握手協議、警告協議、變更密碼規範協議、擴展協議等幾個子協議組成,綜合使用了對稱加密、非對稱加密、身份認證等許多密碼學前沿技術。

瀏覽器和服務器在使用 TLS 建立連接時需要選擇一組恰當的加密算法來實現安全通信,這些算法的組合被稱為密碼套件(cipher suite,也叫加密套件)。

TLS 的密碼套件命名非常規範,格式很固定。基本的形式是密鑰交換算法 + 簽名算法 + 對稱加密算法 + 摘要算法,比如剛才的密碼套件的意思就是:

握手時使用 ECDHE 算法進行密鑰交換,用 RSA 簽名和身份認證,握手後的通信使用 AES 對稱算法,密鑰長度 256 位,分組模式是 GCM,摘要算法 SHA384 用於消息認證和產生隨機數。

4. OpenSSL

說到 TLS,就不能不談到 OpenSSL,它是一個著名的開源密碼學程序庫和工具包,幾乎支持所有公開的加密算法和協議,已經成為了事實上的標準,許多應用軟件都會使用它作為底層庫來實現 TLS 功能,包括常用的 Web 服務器 Apache、Nginx 等。

OpenSSL 是從另一個開源庫 SSLeay 發展出來的,曾經考慮命名為 OpenTLS,但當時(1998 年)TLS 還未正式確立,而 SSL 早已廣為人知,所以最終使用了 OpenSSL 的名字。OpenSSL 目前有三個主要的分支,1.0.2 和 1.1.0 都將在今年(2019)年底不再維護,最新的長期支持版本是 1.1.1。

由於 OpenSSL 是開源的,所以它還有一些代碼分支,比如 Google 的 BoringSSL、OpenBSD 的 LibreSSL,這些分支在 OpenSSL 的基礎上刪除了一些老舊代碼,也增加了一些新特性,雖然背後有 大金主,但離取代 OpenSSL 還差得很遠。

5. 機密性實現-加密

實現機密性最常用的手段是 加密(encrypt),就是把消息用某種方式轉換成誰也看不懂的亂碼,只有掌握特殊鑰匙的人才能再轉換出原始文本。

這裡的鑰匙就叫做 密鑰(key),加密前的消息叫明文(plain text/clear text),加密後的亂碼叫 密文(cipher text),使用密鑰還原明文的過程叫 解密(decrypt)。

所有的加密算法都是公開的,任何人都可以去分析研究,而算法使用的 密鑰 則必須保密。這個關鍵的 密鑰 又是什麼呢?由於 HTTPS、TLS 都運行在計算機上,所以 密鑰 就是一長串的數字,但約定俗成的度量單位是 (bit),而不是 位元組(byte)。比如,說密鑰長度是 128,就是 16 位元組的二進制串,密鑰長度 1024,就是 128 位元組的二進制串。

按照密鑰的使用方式,加密可以分為兩大類:對稱加密 和 非對稱加密

對稱加密

對稱加密 很好理解,就是指加密和解密時使用的密鑰都是同一個,是 對稱 的。只要保證了密鑰的安全,那整個通信過程就可以說具有了機密性。

舉個例子,你想要登錄某網站,只要事先和它約定好使用一個對稱密碼,通信過程中傳輸的全是用密鑰加密後的密文,只有你和網站才能解密。黑客即使能夠竊聽,看到的也只是亂碼,因為沒有密鑰無法解出明文,所以就實現了機密性。

3

TLS 里有非常多的對稱加密算法可供選擇,比如 RC4、DES、3DES、AES、ChaCha20 等,但前三種算法都被認為是不安全的,通常都禁止使用,目前常用的有 AES-128、AES-192、AES-256 和 ChaCha20。

DES 的全稱是 Data Encryption Standard(數據加密標準) ,它是用於數字數據加密的對稱密鑰算法。儘管其 56 位的短密鑰長度使它對於現代應用程序來說太不安全了,但它在加密技術的發展中具有很大的影響力。

AES 的意思是 高級加密標準(Advanced Encryption Standard),AES-128, AES-192 和 AES-256 都是屬於 AES 。密鑰長度可以是 128、192 或 256。它是 DES 算法的替代者,安全強度很高,性能也很好,而且有的硬件還會做特殊優化,所以非常流行,是應用最廣泛的對稱加密算法。

ChaCha20 是 Google 設計的另一種加密算法,密鑰長度固定為 256 位,純軟件運行性能要超過 AES,曾經在移動客戶端上比較流行,但 ARMv8 之後也加入了 AES 硬件優化,所以現在不再具有明顯的優勢。

分組模式

對稱算法還有一個 分組模式 的概念,它可以讓算法用固定長度的密鑰加密任意長度的明文,把小秘密(即密鑰)轉化為大秘密(即密文)。

最早有 ECB、CBC、CFB、OFB 等幾種分組模式,但都陸續被發現有安全漏洞,所以現在基本都不怎麼用了。最新的分組模式被稱為 AEAD(Authenticated Encryption with Associated Data),在加密的同時增加了認證的功能,常用的是 GCM、CCM 和 Poly1305。

把上面這些組合起來,就可以得到 TLS 密碼套件中定義的對稱加密算法。

比如,AES128-GCM,意思是密鑰長度為 128 位的 AES 算法,使用的分組模式是 GCM;ChaCha20-Poly1305 的意思是 ChaCha20 算法,使用的分組模式是 Poly1305。

非對稱加密

對稱加密看上去好像完美地實現了機密性,但其中有一個很大的問題:如何把密鑰安全地傳遞給對方,術語叫 密鑰交換。因為在對稱加密算法中只要持有密鑰就可以解密。如果你和網站約定的密鑰在傳遞途中被黑客竊取,那他就可以在之後隨意解密收發的數據,通信過程也就沒有機密性可言了。

這個問題該怎麼解決呢?一般來說除非是雙方私下約定好了密鑰,如果是每次通信都會發生變化的密鑰是不能在通信過程中帶給對端,這樣你就陷入了要給密鑰加一次密的尷尬境地。所以,就出現了非對稱加密(也叫公鑰加密算法)。

它有兩個密鑰,一個叫 公鑰(public key),一個叫 私鑰(private key)。兩個密鑰是不同的,不對稱,公鑰可以公開給任何人使用,而私鑰必須嚴格保密。

公鑰和私鑰有個特別的 單向 性,雖然都可以用來加密解密,但公鑰加密後只能用私鑰解密,反過來,私鑰加密後也只能用公鑰解密。

非對稱加密可以解決 密鑰交換 的問題。網站秘密保管私鑰,在網上任意分發公鑰,你想要登錄網站只要用公鑰加密就行了,密文只能由私鑰持有者才能解密。而黑客因為沒有私鑰,所以就無法破解密文。

4

非對稱加密算法的設計要比對稱算法難得多,在 TLS 里只有很少的幾種,比如 DH、DSA、RSA、ECC 等。

RSA 可能是其中最著名的一個,幾乎可以說是非對稱加密的代名詞,它的安全性基於 整數分解 的數學難題,使用兩個超大素數的乘積作為生成密鑰的材料,想要從公鑰推算出私鑰是非常困難的。

10 年前 RSA 密鑰的推薦長度是 1024,但隨着計算機運算能力的提高,現在 1024 已經不安全,普遍認為至少要 2048 位。

ECC(Elliptic Curve Cryptography)是非對稱加密里的 後起之秀,它基於 橢圓曲線離散對數 的數學難題,使用特定的曲線方程和基點生成公鑰和私鑰,子算法 ECDHE 用於密鑰交換,ECDSA 用於數字簽名。

ECDHE 即使用橢圓曲線(ECC)的 DH 算法,優點是能用較小的素數(256 位)實現 RSA 相同的安全等級。缺點是算法實現複雜,用於密鑰交換的歷史不長,沒有經過長時間的安全攻擊測試。

目前比較常用的兩個曲線是 P-256(secp256r1,在 OpenSSL 稱為 prime256v1)和 x25519。P-256 是 NIST(美國國家標準技術研究所)和 NSA(美國國家安全局)推薦使用的曲線,而 x25519 被認為是最安全、最快速的曲線。

比起 RSA,ECC 在安全強度和性能上都有明顯的優勢。160 位的 ECC 相當於 1024 位的 RSA,而 224 位的 ECC 則相當於 2048 位的 RSA。因為密鑰短,所以相應的計算量、消耗的內存和帶寬也就少,加密解密的性能就上去了,對於現在的移動互聯網非常有吸引力。

混合加密

看到這裡你是不是認為可以拋棄對稱加密,只用非對稱加密來實現機密性呢?

很遺憾,雖然非對稱加密沒有 密鑰交換 的問題,但因為它們都是基於複雜的數學難題,運算速度很慢,即使是 ECC 也要比 AES 差上好幾個數量級。如果僅用非對稱加密,雖然保證了安全,但通信速度有如烏龜、蝸牛,實用性就變成了零。

是不是能夠把對稱加密和非對稱加密結合起來呢,兩者互相取長補短,即能高效地加密解密,又能安全地密鑰交換。

這就是現在 TLS 里使用的 混合加密 方式,其實說穿了也很簡單:

在通信剛開始的時候使用非對稱算法,比如 RSA、ECDHE,首先解決密鑰交換的問題。

然後用隨機數產生對稱算法使用的 會話密鑰(session key),再用公鑰加密。因為會話密鑰很短,通常只有 16 位元組或 32 位元組,所以慢一點也無所謂。

對方拿到密文後用私鑰解密,取出會話密鑰。這樣,雙方就實現了對稱密鑰的安全交換,後續就不再使用非對稱加密,全都使用對稱加密。

5

這樣混合加密就解決了對稱加密算法的密鑰交換問題,而且安全和性能兼顧,完美地實現了機密性。

6. 完整性

摘要算法

實現完整性的手段主要是 摘要算法(Digest Algorithm),也就是常說的散列函數、哈希函數(Hash Function)。

你可以把摘要算法近似地理解成一種特殊的壓縮算法,它能夠把任意長度的數據壓縮成固定長度、而且獨一無二的摘要字符串,就好像是給這段數據生成了一個數字指紋。

換一個角度,也可以把摘要算法理解成特殊的單向加密算法,它只有算法,沒有密鑰,加密後的數據無法解密,不能從摘要逆推出原文。

摘要算法實際上是把數據從一個 大空間 映射到了 小空間,所以就存在 衝突(collision,也叫碰撞)的可能性,就如同現實中的指紋一樣,可能會有兩份不同的原文對應相同的摘要。好的摘要算法必須能夠 抵抗衝突,讓這種可能性盡量地小。

因為摘要算法對輸入具有 單向性雪崩效應,輸入的微小不同會導致輸出的劇烈變化,所以也被 TLS 用來生成偽隨機數(PRF,pseudo random function)。

你一定在日常工作中聽過、或者用過 MD5(Message-Digest 5)、SHA-1(Secure Hash Algorithm 1),它們就是最常用的兩個摘要算法,能夠生成 16 位元組和 20 位元組長度的數字摘要。但這兩個算法的安全強度比較低,不夠安全,在 TLS 里已經被禁止使用了。

目前 TLS 推薦使用的是 SHA-1 的後繼者:SHA-2。

SHA-2 實際上是一系列摘要算法的統稱,總共有 6 種,常用的有 SHA224、SHA256、SHA384,分別能夠生成 28 位元組、32 位元組、48 位元組的摘要。

摘要算法保證了 數字摘要 和原文是完全等價的。所以,我們只要在原文後附上它的摘要,就能夠保證數據的完整性。

比如,你發了條消息:有內鬼,終止交易,然後再加上一個 SHA-2 的摘要。網站收到後也計算一下消息的摘要,把這兩份 指紋 做個對比,如果一致,就說明消息是完整可信的,沒有被修改。

如果黑客在中間哪怕改動了一個標點符號,摘要也會完全不同,網站計算比對就會發現消息被竄改,是不可信的。

不過摘要算法不具有機密性,如果明文傳輸,那麼黑客可以修改消息後把摘要也一起改了,網站還是鑒別不出完整性。

所以,真正的完整性必須要建立在機密性之上,在混合加密系統里用會話密鑰加密消息和摘要,這樣黑客無法得知明文,也就沒有辦法動手腳了。這有個術語,叫哈希消息認證碼(HMAC)。

6

數字簽名

加密算法結合摘要算法,我們的通信過程可以說是比較安全了。但這裡還有漏洞,就是通信的兩個端點(endpoint)。

就像一開始所說的,黑客可以偽裝成網站來竊取信息。而反過來,他也可以偽裝成你,向網站發送支付、轉賬等消息,網站沒有辦法確認你的身份,錢可能就這麼被偷走了。

現實生活中,解決身份認證的手段是簽名和印章,只要在紙上寫下簽名或者蓋個章,就能夠證明這份文件確實是由本人而不是其他人發出的。

回想一下上面的介紹在 TLS 里有什麼東西和現實中的簽名、印章很像,只能由本人持有,而其他任何人都不會有呢?只要用這個東西,就能夠在數字世界裏證明你的身份。

沒錯,這個東西就是非對稱加密里的 私鑰,使用私鑰再加上摘要算法,就能夠實現 數字簽名,同時實現 身份認證不可否認

數字簽名的原理其實很簡單,就是把公鑰私鑰的用法反過來,之前是公鑰加密、私鑰解密,現在是私鑰加密、公鑰解密。

但又因為非對稱加密效率太低,所以私鑰只加密原文的摘要,這樣運算量就小的多,而且得到的數字簽名也很小,方便保管和傳輸。

簽名和公鑰一樣完全公開,任何人都可以獲取。但這個簽名只有用私鑰對應的公鑰才能解開,拿到摘要後,再比對原文驗證完整性,就可以像簽署文件一樣證明消息確實是你發的。

7

剛才的這兩個行為也有專用術語,叫做 簽名驗簽

只要你和網站互相交換公鑰,就可以用 簽名驗簽 來確認消息的真實性,因為私鑰保密,黑客不能偽造簽名,就能夠保證通信雙方的身份。

比如,你用自己的私鑰簽名一個消息 馬冬梅你別跑。網站收到後用你的公鑰驗簽,確認身份沒問題,於是也用它的私鑰簽名消息 十年翻身同學會,拆散一對是一對。你收到後再用它的公鑰驗一下,也沒問題,這樣你和網站就都知道對方不是假冒的,後面就可以用混合加密進行安全通信了。

有關加解密相關的算法,可以看我另一篇博文:

加解密算法淺析

可以幫助大家對加解密有一個統一的認識。

7. 數字證書和 CA–身份認證

到現在,綜合使用對稱加密、非對稱加密和摘要算法,我們已經實現了安全的四大特性,是不是已經完美了呢?

不是的,這裡還有一個 公鑰的信任 問題。因為誰都可以發佈公鑰,我們還缺少防止黑客偽造公鑰的手段,也就是說,怎麼來判斷這個公鑰就是你的公鑰呢?

我們可以用類似密鑰交換的方法來解決公鑰認證問題,用別的私鑰來給公鑰簽名,顯然,這又會陷入俄羅斯套娃

但這次實在是沒招了,要終結這個死循環,就必須引入外力,找一個公認的可信第三方,讓它作為信任的起點,遞歸的終點,構建起公鑰的信任鏈。

這個第三方就是我們常說的 CA(Certificate Authority,證書認證機構)。它就像網絡世界裏的公安局、教育部、公證中心,具有極高的可信度,由它來給各個公鑰簽名,用自身的信譽來保證公鑰無法偽造,是可信的。

CA 對公鑰的簽名認證也是有格式的,不是簡單地把公鑰綁定在持有者身份上就完事了,還要包含序列號、用途、頒發者、有效時間等等,把這些打成一個包再簽名,完整地證明公鑰關聯的各種信息,形成 數字證書(Certificate)。

知名的 CA 全世界就那麼幾家,比如 DigiCert、VeriSign、Entrust、Let』s Encrypt 等,它們簽發的證書分 DV、OV、EV 三種,區別在於可信程度。

DV 是最低的,只是域名級別的可信,背後是誰不知道。EV 是最高的,經過了法律和審計的嚴格核查,可以證明網站擁有者的身份(在瀏覽器地址欄會顯示出公司的名字,例如 Apple、GitHub 的網站)。

不過,CA 怎麼證明自己呢?

這還是信任鏈的問題。小一點的 CA 可以讓大 CA 簽名認證,但鏈條的最後,也就是Root CA,就只能自己證明自己了,這個就叫 自簽名證書(Self-Signed Certificate)或者 根證書(Root Certificate)。你必須相信,否則整個證書信任鏈就走不下去了。

8

有了這個證書體系,操作系統和瀏覽器都內置了各大 CA 的根證書,上網的時候只要服務器發過來它的證書,就可以驗證證書里的簽名,順着證書鏈(Certificate Chain)一層層地驗證,直到找到根證書,就能夠確定證書是可信的,從而裏面的公鑰也是可信的。

證書體系的弱點

證書體系(PKI,Public Key Infrastructure)雖然是目前整個網絡世界的安全基礎設施,但絕對的安全是不存在的,它也有弱點,還是關鍵的 信任 二字。

如果 CA 失誤或者被欺騙,簽發了錯誤的證書,雖然證書是真的,可它代表的網站卻是假的。

還有一種更危險的情況,CA 被黑客攻陷,或者 CA 有惡意,因為它(即根證書)是信任的源頭,整個信任鏈里的所有證書也就都不可信了。

這兩種事情並不是聳人聽聞,都曾經實際出現過。所以需要再給證書體系打上一些補丁。

針對第一種,開發出了 CRL(證書吊銷列表,Certificate revocation list)和 OCSP(在線證書狀態協議,Online Certificate Status Protocol),及時廢止有問題的證書。

對於第二種因為涉及的證書太多,就只能操作系統或者瀏覽器從根上下狠手了,撤銷對 CA 的信任,列入黑名單,這樣它頒發的所有證書就都會被認為是不安全的。

我們來看一下Github的數字證書長什麼樣子:

16

證書上的信息可以得知:類型是 EV,擁有最高權威認證;過期時間;證書所屬組織;證書籤發機構。

8. TLS 協議的組成

當你在瀏覽器地址欄里鍵入 https 開頭的 URI,再按下回車,會發生什麼呢?

瀏覽器首先要從 URI 里提取出協議名和域名。因為協議名是https,所以瀏覽器就知道了端口號是默認的 443,它再用 DNS 解析域名,得到目標的 IP 地址,然後就可以使用三次握手與網站建立 TCP 連接了。

在 HTTP 協議里,建立連接後,瀏覽器會立即發送請求報文。但現在是 HTTPS 協議,它需要再用另外一個握手過程,在 TCP 上建立安全連接,之後才是收發 HTTP 報文。

這個握手過程與 TCP 有些類似,是 HTTPS 和 TLS 協議里最重要、最核心的部分。

在講 TLS 握手之前,先簡單介紹一下 TLS 協議的組成。

TLS 包含幾個子協議,你也可以理解為它是由幾個不同職責的模塊組成,比較常用的有:記錄協議、警報協議、握手協議、變更密碼規範協議等。

  • 記錄協議(Record Protocol)規定了 TLS 收發數據的基本單位:記錄(record)。它有點像是 TCP 里的 segment,所有的其他子協議都需要通過記錄協議發出。但多個記錄數據可以在一個 TCP 包里一次性發出,也並不需要像 TCP 那樣返回 ACK。
  • 警報協議(Alert Protocol)的職責是向對方發出警報信息,有點像是 HTTP 協議里的狀態碼。比如,protocol_version 就是不支持舊版本,bad_certificate 就是證書有問題,收到警報後另一方可以選擇繼續,也可以立即終止連接。
  • 握手協議(Handshake Protocol)是 TLS 里最複雜的子協議,要比 TCP 的 SYN/ACK 複雜的多,瀏覽器和服務器會在握手過程中協商 TLS 版本號、隨機數、密碼套件等信息,然後交換證書和密鑰參數,最終雙方協商得到會話密鑰,用於後續的混合加密系統。
  • 變更密碼規範協議(Change Cipher Spec Protocol),它非常簡單,就是一個通知,告訴對方,後續的數據都將使用加密保護。那麼反過來,在它之前,數據都是明文的。

下面的這張圖簡要地描述了 TLS 的握手過程,其中每一個都是一個記錄,多個記錄組合成一個 TCP 包發送。所以,最多經過兩次消息往返(4 個消息)就可以完成握手,然後就可以在安全的通信環境里發送 HTTP 報文,實現 HTTPS 協議。

在 TCP 完成三次握手建立連接之後, HTTPS 開始加密認證握手流程。 TLS 的握手過程如下:

以上過程我們可以使用 wireShark 抓包工具看到。

在 TCP 建立連接之後,瀏覽器會首先發一個 client_hello 消息,裏面有客戶端的版本號、支持的密碼套件,還有一個隨機數(Client Random),用於後續生成會話密鑰:

17

可以看到客戶端發送給服務端他所支持的密碼套件有 16 套之多,另外客戶端使用的 TLS 版本是1.2。服務器收到 Client Hello 後,會返回一個 Server Hello 消息。把版本號對一下,也給出一個隨機數(Server Random),然後從客戶端的列表裡選一個作為本次通信使用的密碼套件,在這裡它選擇了Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

18

然後服務器為了證明自己的身份,就把證書也發給了客戶端(Server Certificate)。

19

接下來是一個關鍵的操作,因為服務器選擇了 ECDHE 算法,所以它會在證書後發送 Server Key Exchange 消息,裏面是 橢圓曲線的公鑰(Server Params),用來實現密鑰交換算法,再加上自己的私鑰簽名認證。

Handshake Protocol: Server Key Exchange
    EC Diffie-Hellman Server Params
        Curve Type: named_curve (0x03)
        Named Curve: x25519 (0x001d)
        Pubkey: 3b39deaf00217894e...
        Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
        Signature: 37141adac38ea4...

這相當於說:剛才我選的密碼套件有點複雜,所以再給你個算法的參數,和剛才的隨機數一樣有用別丟了。為了防止別人冒充,我又蓋了個章。

之後是 Server Hello Done 消息,服務器說:我的信息就是這些,打招呼完畢。

這樣第一個消息往返就結束了(兩個 TCP 包),結果是客戶端和服務器通過明文共享了三個信息:Client Random、Server Random 和 Server Params

客戶端這時也拿到了服務器的證書,那這個證書是不是真實有效的呢?下面客戶端就開始鑒定證書的真偽。校驗過程如下:

  1. 首先讀取證書所有者有效期等信息進行校驗,查找內置的收信人證書發佈機構 CA 與證書 CA 相對比,校驗是否是合法機構頒發;這一步會做如下操作:

    1. 證書鏈的可信性 (trusted certificate path)校驗,發行服務器證書的 CA 是否可靠;
    2. 證書是否吊銷 (revocation),有兩類方式離線 CRL 與在線 OCSP,不同的客戶端行為會不同;
    3. 有效期 (expiry date),證書是否在有效時間範圍;
    4. 域名 (domain),核查證書域名是否與當前的訪問域名匹配,匹配規則後續分析。
  2. 第一步檢驗通過之後產生隨機數 Pre-master, 並用證書公鑰加密,發送給服務器,作為以後對稱加密的密鑰。

客戶端向服務器發送 Client Key Exchange。最後客戶端與服務器互發 Change Cipher Spec,Encrypted Handshake Message

20

接下來服務器接收到客戶端發送的 Pre-master,解密出被加密的 Pre-master,然後通知客戶端:

握手階段結束,隨後所有的通信將使用對稱加密的方式進行。

9. 雙向認證

上面已經講完了 TLS 握手,從上面的流程不難看出只是客戶端認證了服務器的身份,而服務器是沒有認證客戶端身份的,我們簡稱 單向認證。通常單向認證通過後已經建立了安全通信,用賬號、密碼等簡單的手段就能夠確認用戶的真實身份。但為了防止賬號、密碼被盜,有的時候(比如網上銀行)還會使用 U 盾給用戶頒發客戶端證書,實現雙向認證,這樣會更加安全。

雙向認證的流程也沒有太多變化,只是在 Server Hello Done 之後,Client Key Exchange 之前,客戶端要發送 Client Certificate 消息,服務器收到後也把證書鏈走一遍,驗證客戶端的身份。

不過 TLS1.2 已經是 10 年前(2008 年)的協議了,雖然歷經考驗但畢竟 歲月不饒人,在安全、性能等方面已經跟不上如今的互聯網。經過四年近 30 個草案的反覆打磨,TLS1.3 在2018 年推出,再次確立了信息安全領域的新標準。

10. 最大化兼容性

由於 1.1、1.2 等協議已經出現了很多年,很多應用軟件、中間代理(官方稱為MiddleBox)只認老的記錄協議格式,更新改造很困難,甚至是不可行(設備僵化)。

在早期的試驗中發現,一旦變更了記錄頭字段里的版本號,也就是由 0x303(TLS1.2)改為 0x304(TLS1.3)的話,大量的代理服務器、網關都無法正確處理,最終導致 TLS 握手失敗。

為了保證這些被廣泛部署的老設備能夠繼續使用,避免新協議帶來的衝擊,TLS1.3 不得不做出妥協,保持現有的記錄格式不變,通過偽裝來實現兼容,使得 TLS1.3 看上去像是 TLS1.2。

那麼,該怎麼區分 1.2 和 1.3 呢?

這要用到一個新的 擴展協議(Extension Protocol),它有點 補充條款 的意思,通過在記錄末尾添加一系列的 擴展字段 來增加新的功能,老版本的 TLS 不認識它可以直接忽略,這就實現了後向兼容。

在記錄頭的 Version 字段被兼容性固定的情況下,只要是 TLS1.3 協議,握手的 Hello 消息後面就必須有 supported_versions 擴展,它標記了 TLS 的版本號,使用它就能區分新舊協議。

你可以看一下Client Hello消息後面的擴展,只是因為服務器不支持 1.3,所以就後向兼容降級成了 1.2。

21

TLS1.3 利用擴展實現了許多重要的功能,比如supported_groupskey_sharesignature_algorithmsserver_name等,這些等後面用到的時候再說。

11. 強化安全

TLS1.2 在十來年的應用中獲得了許多寶貴的經驗,陸續發現了很多的漏洞和加密算法的弱點,所以 TLS1.3 就在協議里修補了這些不安全因素。

比如:

  • 偽隨機數函數由 PRF 升級為 HKDF(HMAC-based Extract-and-Expand Key Derivation Function);
  • 明確禁止在記錄協議里使用壓縮;
  • 廢除了 RC4、DES 對稱加密算法;
  • 廢除了 ECB、CBC 等傳統分組模式;
  • 廢除了 MD5、SHA1、SHA-224 摘要算法;
  • 廢除了 RSA、DH 密鑰交換算法和許多命名曲線。

經過這一番減肥瘦身之後,TLS1.3 里只保留了 AES、ChaCha20 對稱加密算法,分組模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法只能用 SHA256、SHA384,密鑰交換算法只有 ECDHE 和 DHE,橢圓曲線也被到只剩 P-256 和 x25519 等 5 種。

算法精簡後帶來了一個意料之中的好處:原來眾多的算法、參數組合導緻密碼套件非常複雜,難以選擇,而現在的 TLS1.3 里只有 5 個套件,無論是客戶端還是服務器都不會再犯選擇困難症了。

密碼套件名稱 代碼
Cipher Suite: TLS_AES_128_GCM_SHA256 0x1301
Cipher Suite: TLS_AES_256_GCM_SHA384 0x1302
Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 0x1303
TLS-AES128-CCM-SHA256 0x1304
TLS-AES128-CCM-8-SHA256 0x1305

這裡還要特別說一下廢除 RSA 和 DH 密鑰交換算法的原因。

在 RSA 密鑰交換中,共享密鑰由客戶端生成,然後客戶端利用服務器的公鑰(從證書中提取)將共享密鑰加密並將其發送到服務器。

DH 算法由 DiffieHellman 於1976年發明,即所謂的 Diffie-Hellman 密鑰交換。在 Diffie-Hellman 中,客戶端和服務器都從創建 DH 參數對開始。然後,他們將其 DH 參數的公共部分發送給另一方。當雙方都收到對方方的公共參數時,它們將它與自己的私鑰組合在一起,最終計算出同一個值:前主密鑰。然後,服務器使用數字簽名來確保交換未被篡改。如果客戶端和服務器都為每次密鑰交換選擇一個新的 DH 參數,則該密鑰交換稱為 「Ephemeral」(DHE)。

DH 是一個功能強大的工具,但並非所有DH參數都可以 「安全」 使用。DH 的安全性取決於稱為數學中離散對數問題的難度。如果可以解決一組參數的離散對數問題,就可以提取私鑰並破壞協議的安全性。一般來說,使用的數字越大,解決離散對數問題就越困難。因此,如果您選擇較小的DH參數,就有可能遭受攻擊。

上兩種模式都會使客戶端和服務器具有共享密鑰,但 RSA 模式有一個嚴重的缺點,這是因為它不具有 前向安全(Forward Secrecy)。

假設有這麼一個很有耐心的黑客,一直在長期收集混合加密系統收發的所有報文。如果加密系統使用服務器證書里的 RSA 做密鑰交換,一旦私鑰泄露或被破解(使用社會工程學或者巨型計算機),那麼黑客就能夠使用私鑰解密出之前所有報文的 Pre-Master,再算出會話密鑰,破解所有密文。

而 ECDHE 算法在每次握手時都會生成一對臨時的公鑰和私鑰,每次通信的密鑰對都是不同的,也就是 一次一密,即使黑客花大力氣破解了這一次的會話密鑰,也只是這次通信被攻擊,之前的歷史消息不會受到影響,仍然是安全的。

所以現在主流的服務器和瀏覽器在握手階段都已經不再使用 RSA,改用 ECDHE,而 TLS1.3 在協議里明確廢除 RSA 和 DH 則在標準層面保證了前向安全。

RSA 密鑰交換在一段時間內一直存在問題,其原因不僅僅是因為它支持前向保密。而是因為想要正確的實現 RSA 密鑰交換也是不容易的。1998年,Daniel Bleichenbacher 在 SSL 中使用 RSA 加密時發現了一個漏洞並創建了所謂的 「百萬消息攻擊」, 它允許攻擊者通過發送數百萬條消息或一些特定的消息給服務器,根據服務器響應的不同錯誤碼計算加密密鑰, 進而解密消息。多年來,這種攻擊得到了改進,在某些情況下只需要數千次就可破解,這使得在筆記本電腦上都可以破解。最近發現,許多大型網站(包括facebook.com)在2017年也受到 Bleichenbacher 變種漏洞的影響,即ROBOT攻擊

為了降低非前向加密連接和 Bleichenbacher 漏洞所帶來的風險,RSA 加密已從 TLS 1.3 中刪除,將 Diffie-Hellman Ephemeral 作為唯一的密鑰交換機制。

12. 提升性能

HTTPS 建立連接時除了要做 TCP 握手,還要做 TLS 握手,在 1.2 中會多花兩個消息往返(2-RTT),可能導致幾十毫秒甚至上百毫秒的延遲,在移動網絡中延遲還會更嚴重。

現在因為密碼套件大幅度簡化,也就沒有必要再像以前那樣走複雜的協商流程了。TLS1.3 壓縮了以前的 Hello 協商過程,刪除了 Key Exchange 消息,把握手時間減少到了 1-RTT,效率提高了一倍。

那麼它是怎麼做的呢?其實具體的做法還是利用了擴展。客戶端在 Client Hello 消息里直接用 supported_groups 帶上支持的曲線,比如 P-256、x25519,用 key_share帶上曲線對應的客戶端公鑰參數,用signature_algorithms帶上簽名算法。

服務器收到後在這些擴展里選定一個曲線和參數,再用key_share 擴展返回服務器這邊的公鑰參數,就實現了雙方的密鑰交換,後面的流程就和 1.2 基本一樣了。

除了標準的 1-RTT 握手,TLS1.3 還引入了0-RTT 握手,用 pre_shared_keyearly_data 擴展,在 TCP 連接後立即就建立安全連接發送加密消息,不過這需要有一些前提條件,限於篇幅這裡暫且不表。

13. HTTPS使用成本

HTTPS 截止到目前為止國內的大中型企業基本都支持並已經使用。一般來講,使用 HTTPS 前大家可能會非常關注如下問題:

  1. 證書費用以及更新維護。費用主要是證書的申請,而且現在也有了免費的證書機構,比如著名的 mozilla 發起的免費證書項目:let』s encrypt 就支持免費證書安裝和自動更新。
  2. HTTPS 降低用戶訪問速度。HTTPS 對速度會有一定程度的降低,但是只要經過合理優化和部署,HTTPS 對速度的影響完全可以接受。在很多場景下,HTTPS 速度完全不遜於 HTTP,如果使用 SPDY,HTTPS 的速度甚至還要比 HTTP 快。