安全傳輸層協(xié)議(TLS)是如何保證安全的?
上篇主要是介紹了HTTP存在的兩大安全問題
- 明文
- 無法驗證服務(wù)器的真實性
從而引出了TLS。本篇就來著重介紹下TLS。
說起TLS可能有些人還比較陌生,但如果說到SSL,那知道的人就更多了。TLS其實就是SSL發(fā)展而來,版本演進(jìn)大體為SSL 2.0 -> SSL 3.0 -> TLS 1.0(可以看做是SSL 3.1版)。
TLS主要提供三個基本服務(wù)
- 加密
- 身份驗證
- 消息完整性校驗
其中第三點是網(wǎng)絡(luò)協(xié)議中常用的一個校驗和機制,和我們這邊要說的安全話題不是太相關(guān),不再贅述。而前兩點正好是對應(yīng)了HTTP的兩個問題,下面分別介紹下。
內(nèi)容加密
其實網(wǎng)絡(luò)通信可以類比為人之間的對話,比如A和B之間需要溝通,但兩人沒法直接見面,但有一個傳話人可以代為傳話。這樣兩人就能間接溝通了,只不過這個傳話的過程是有一定風(fēng)險(被記錄,被篡改)的。
為了避免這種風(fēng)險,久而久之A和B想出了一個辦法:他們約定使用傳話人不會的上海話溝通。這樣每次傳話人都聽不懂啥意思,只能盡可能有樣學(xué)樣地把A說給他的話學(xué)給B聽。
這個普通話翻譯成上海話的過程,對于傳話人來說就是一種內(nèi)容的『加密』。但這個方法不一定是唯一的,可能以后另一個傳話人懂上海話呢?于是在每次使用方言溝通之前,A和B會先行溝通下他們這次對話使用的語種(兩人真是牛逼!)
這個選擇語種的過程,其實就是兩人之間關(guān)于翻譯方式的『協(xié)商』過程。傳話人要想從中搗鬼,就能立馬被發(fā)現(xiàn),這樣AB兩人為了溝通安全就會停止這次溝通,轉(zhuǎn)而找其他的傳話人傳話了。
以上看到AB兩人機智的采用『翻譯』『協(xié)商』兩招完成的安全溝通了。將同樣的方法拿到網(wǎng)絡(luò)通信中,就是TLS的加密機制了。
這個圖描述了發(fā)送端和接收端是怎么協(xié)商他們之間的加密機制的。TLS協(xié)議是基于TCP協(xié)議之上的,圖中第一個藍(lán)色往返是TCP的握手過程,不在本文范疇中按下不表。之后的兩次橙色的往返,就是我們要重點說的協(xié)商過程了,可以叫做TLS的握手。握手過程如下:
- Client1:TLS版本號 + 所支持加密套件列表 + 希望使用的TLS選項
- Server1:選擇一個客戶端的加密套件 + 自己的公鑰 + 自己的證書 + 希望使用的TLS選項 +(要求客戶端證書)
- Client2:(自己的證書) + 使用服務(wù)器公鑰和協(xié)商的加密套件加密一個對稱密鑰
- Server2:使用私鑰解密出對稱密鑰后,發(fā)送的加密的Finish消息,表明完成握手
我們一步一步來仔細(xì)看。
第一步,客戶說明他支持的所有加密套件(就像上海話、廣東話一樣)讓服務(wù)端選擇一個。這里和Server1還完成了一個TLS選項的協(xié)商,這可以理解為在確定核心的加密方式的同時,順便溝通下是否能支持一些『附加功能』。這些附加功能先按下不表,后續(xù)會介紹兩個常用的。
第二步,服務(wù)端選擇了加密套件。加密算法都是需要一個秘鑰的,服務(wù)端這里給出的是自己的公鑰。這里的公鑰其實蘊含著一個關(guān)鍵點:秘鑰協(xié)商過程采用了不對稱加密。簡單來說,一般的對稱加密如下
encrypt(明文,秘鑰) = 密文
decrypt(密文,秘鑰) = 明文
也就是說加密和解密用的是一個秘鑰。而非對稱加密就比較神奇了
encrypt(明文,公鑰) = 密文
decrypt(密文,私鑰) = 明文
加密和解密是需要不同的密鑰的。在TLS握手前,客戶端本身不知道服務(wù)器的秘鑰的,如果運用對稱加密,那么服務(wù)端告訴客戶端秘鑰的過程中,兩者之間的中間節(jié)點其實也拿到秘鑰了,這樣后續(xù)兩者之間的加密通信,對于中間節(jié)點來說也能解密出內(nèi)容了。因此這里采用不對稱加密,服務(wù)端給出一個公鑰供客戶端后續(xù)使用。此外這一步服務(wù)端還提供了證書,并且可能要求客戶端提供證書。關(guān)于證書下文會提到,只要有了證書,就能保證和你通信的對方是真實的,而不是別人偽造的。這里只要先理解,客戶端看到服務(wù)端證書后,就能夠相信服務(wù)端并使用服務(wù)端給的公鑰了。
第三步,客戶端給出了雙方后續(xù)加密通信所要使用的對稱密鑰,通過服務(wù)器公鑰加密后返回給服務(wù)端。由于對稱密鑰是被不對稱加密算法加密起來的,因此中間節(jié)點拿到后,沒有辦法解密出真正的內(nèi)容。
第四步,服務(wù)端拿公鑰對應(yīng)的私鑰解密出真正的對稱密鑰,至此加密算法和加密所需的密鑰都確定,服務(wù)端給客戶端發(fā)送一個finish完成握手,證明雙方都已經(jīng)準(zhǔn)備好加密通信了。之后雙方的交互都可以使用對稱加密算法加密了。
TLS擴展
說完了如何確定加密的算法和密鑰后,我們再說說那所謂的『附加功能』。所謂TLS的擴展,就是在TLS協(xié)議核心保持不變的基礎(chǔ)上,對其進(jìn)行擴展以使其支持更多的特性,這里簡單介紹下兩個擴展:ALPN和SNI。
ALPN(Application Layer Protocol Negotiation)即應(yīng)用層協(xié)議協(xié)商,旨在將原來應(yīng)用層協(xié)議中的協(xié)議協(xié)商,提前合并到TLS握手時完成,減少一次往返時間。這里通過一個例子來說明下什么叫做應(yīng)用層的協(xié)議協(xié)商。假設(shè)TLS握手完成了,接下來客戶端和服務(wù)端就應(yīng)該開始進(jìn)行正常的HTTP通信了,但是客戶端認(rèn)為HTTP協(xié)議比較慢(還記得上篇提到的流式傳輸?shù)男蕟栴}嗎),在請求內(nèi)容之前先向服務(wù)端提出『我們能不能換HTTP2.0通信』。服務(wù)端會根據(jù)自己支持HTTP2.0的情況給出答復(fù),之后客戶端會根據(jù)服務(wù)端的答復(fù),使用HTTP或HTTP2.0向服務(wù)端請求內(nèi)容??梢钥吹竭@個過程中又多了一次協(xié)商升級協(xié)議的往返,導(dǎo)致客戶端拿到返回結(jié)果的延遲更大了。為了優(yōu)化這一點,在HTTP使用TLS加密的時候,會使用ALPN擴展,把協(xié)商升級協(xié)議的請求放在Client1中,服務(wù)端在Server1就返回說是否可以升級協(xié)議。這樣TLS握手之后,客戶端就可以直接按HTTP2.0發(fā)起請求而不必多一次往返了。
SNI(Server Name Indication)叫做服務(wù)器名稱指示,主要是支持服務(wù)端同一個機器部署有多個站點的情況的。前面說的證書、公鑰私鑰,都是針對某個站點來說的。網(wǎng)站A和網(wǎng)站B可能租了同一臺服務(wù)器,但他們的證書和密鑰一定是不同的。當(dāng)客戶端請求到這臺公用服務(wù)器時,服務(wù)器也不知道該返回哪個站點的信息了。這個時候使用SNI擴展,在client1帶上訪問站點的信息,服務(wù)端看到后,就會去找對應(yīng)站點的證書、密鑰了。
TLS的代價
任何事物都是有利有弊,引入TLS機制固然是能夠保證安全,但卻在TCP握手和HTTP通信之間,多加了兩個往返的TLS握手過程。特別是現(xiàn)代網(wǎng)頁應(yīng)用中充滿了AJAX請求的場景下,請求的往往都是一個很小的資源,在一波TCP包中就能返回的。這種情況下握手需要三次往返,而真正有意義的請求往返只有一次,安全的代價非常大!(關(guān)于延遲對于性能的重要性,怎么強調(diào)都不為過,但往往會被人所忽視。這里不展開,但可以仔細(xì)思考下TCP的擁塞避免策略:在大延遲的情況下TCP包需要等上一波TCP包的往返后才會繼續(xù)發(fā)送。這就是為什么很多人都是50兆100兆的寬帶還經(jīng)常覺得上網(wǎng)慢的原因,因為延遲大!而運營商是從來不會宣傳或保證延遲這個指標(biāo)的)
當(dāng)然,還是有優(yōu)化的辦法的。上篇說過,現(xiàn)代web應(yīng)用經(jīng)常是打開一個網(wǎng)頁同時會發(fā)送幾十個請求。TLS握手這個協(xié)商加密套件和密鑰的過程,需要重復(fù)幾十次嗎?當(dāng)然不用。既然這幾十個請求都是請求的同一個網(wǎng)站,那共用第一次請求的協(xié)商結(jié)果就好了。這樣雖然第一次請求中TLS握手的代價很大(比如占了50%),但從整個網(wǎng)頁全局看來,代價就降低到了百分之幾了。目前一些主流瀏覽器的實現(xiàn)也確實利用了這個機制:他們會刻意先發(fā)起一個請求,待該請求的TLS鏈接完成后,再并發(fā)發(fā)起其他的請求。后續(xù)并發(fā)的請求就能夠利用TLS協(xié)商后的結(jié)果了。
這種對協(xié)商結(jié)果的復(fù)用叫做TLS的『會話恢復(fù)機制』,主要有兩種方案:Session Identifier 和 Session Ticket。
Session Identifier即會話標(biāo)識符,是一種服務(wù)端保存會話信息的方案。
- 服務(wù)端為每個客戶端保存會話ID和協(xié)商后的會話參數(shù)
- 會話ID通過Server1帶給客戶端
- 客戶端在后續(xù)創(chuàng)建連接的Client1中帶上會話ID
- 如果兩方都還記得會話ID,則可開啟『簡短握手』,一次往返即可
- 如該會話ID的信息在服務(wù)端不存在,則繼續(xù)正常握手的第二個往返
方案整體思路其實和web應(yīng)用的session類似,都是在服務(wù)端維護(hù)信息,客戶端提供一個sessionId。因此該方案面臨和session一樣的挑戰(zhàn):服務(wù)端需要管理海量會話信息并且制定適當(dāng)?shù)奶蕴呗浴?/p>
Session Ticket即會話記錄單,是一種客戶端保存會話信息的方案。
- 協(xié)商成功后會話信息由服務(wù)器通過其私鑰加密后攜帶在最后一次交換中
- 由客戶端保存這個加密后的會話信息,但其無法解密得知具體內(nèi)容
- 下次連接時客戶端將其帶在Client1的SessionTicket擴展信息中
這個方案的整體思路就和cookie類似了,有效地減輕了服務(wù)端的壓力。但這屬于一種較新的機制,目前還不是所有客戶端都支持。
證書機制
上面這么大篇幅都是介紹的TLS如何完成『加密』這個事情,然而之前說到的HTTP另一個問題『無法驗證服務(wù)器的真實性』還是沒有得到解決。畢竟如果有個節(jié)點要偽裝成服務(wù)器,和用戶進(jìn)行TLS的加解密交互,也是完全沒問題的吧。因此,TLS中還需要通過『證書機制』來保證你所訪問的服務(wù)器是真實的。從安全層面來說,『證書』和『加密』是同等重要的兩個機制。
做個假設(shè),你和小A是朋友,你們之間什么事都可以暢所欲言。你們當(dāng)面聊天時,你不介意把自己的隱私和小A分享。你的分享,其實是建立在『當(dāng)面』的基礎(chǔ)上的,也就是你通過『辨識小A的外貌』來斷定他的真實性后,才會信任他與他分享。如果有一天小A通過一個陌生的電話聯(lián)系你,那你恐怕就不會這么毫無防備了。為了驗證電話那頭真的是小A,你們之間可能需要一些驗證機制,比如讓小A說說你們小時候發(fā)生的某個事或者在你們兩個面對面的時候確定個暗號。這個事件或者暗號,其實就是你用來認(rèn)證小A的『證書』了。 這樣你和小A之間的認(rèn)證就OK了??捎幸惶?,一個自稱小A朋友的小B找到你,想叫你幫個忙,可以你之前從來沒聽說過小B。這個時候為了驗證小B是否是可信的,你只能找來小A幫忙了。你聯(lián)系小A使用你們之間的『證書』驗證其真實性后,由小A使用他和小B約定的『證書』來驗證小B的真實性。驗證成功后,你也就能信任小B了吧,畢竟你的朋友小A已經(jīng)信任電話那頭的小B了。
這個找小A驗證小B的過程,正是TLS中核心的『證書鏈』機制。
前面說到,在TLS握手過程中服務(wù)端會提供給客戶端它的證書。這個證書可不是隨意生成的,而是通過指定的權(quán)威機構(gòu)申請頒發(fā)的。服務(wù)端如果能夠提供一個合法的證書,說明這個服務(wù)端是合法的,可以被信任。就拿上圖來說
- 客戶端獲取到了站點證書,拿到了站點的公鑰
- 要驗證站點可信后,才能使用其公鑰,因此客戶端找到其站點證書頒發(fā)者的信息
- 站點證書的頒發(fā)者驗證了服務(wù)端站點是可信的,但客戶端依然不清楚該頒發(fā)者是否可信
- 再往上回溯,找到了認(rèn)證了中間證書商的源頭證書頒發(fā)者。由于源頭的證書頒發(fā)者非常少,我們?yōu)g覽器之前就認(rèn)識了,因此可以認(rèn)為根證書頒發(fā)者是可信的
- 一路倒推,證書頒發(fā)者可信,那么它所頒發(fā)的所有站點也是可信的,最終確定我們所要訪問的服務(wù)端是可信的
- 客戶端使用證書中包含的公鑰,繼續(xù)完成TLS的握手過程
整個過程包含兩個關(guān)鍵點
- 根證書頒發(fā)機構(gòu)的權(quán)威性需要保證
- 如何從證書頒發(fā)者那里驗證證書的合法性
先說說第一個問題,權(quán)威的根證書頒發(fā)機構(gòu)非常少,因此瀏覽器和操作系統(tǒng)都會內(nèi)置一些可信的根證書頒發(fā)機構(gòu),也就是說這些機構(gòu)的權(quán)威性是由瀏覽器或操作系統(tǒng)保證的。但曾經(jīng)也出現(xiàn)過一些并不權(quán)威的證書由于某些目的被內(nèi)置在中國版的Firefox中這樣的事件,讓人直呼防不勝防。
好在當(dāng)前大多數(shù)情況下跟證書還是能夠保證權(quán)威的,如果是CNNIC這種系統(tǒng)性風(fēng)險,那也只有靠多關(guān)注安全新聞來及時避免了。通過系統(tǒng)或瀏覽器配置就能查看到自己內(nèi)置可信的跟證書頒發(fā)機構(gòu)了,這里是我mac的結(jié)果。如果發(fā)生了像上述CNNIC證書的問題,只要到證書列表這里刪除對應(yīng)的可信證書即可。
再來說說第二個問題,如何從證書頒發(fā)者那里驗證某個證書的有效性。每個證書頒發(fā)機構(gòu)在頒發(fā)證書的同時,有義務(wù)維護(hù)其認(rèn)證站點的權(quán)威性:比如每個證書都有過期時間,一旦到期就要立即失效;某個站點需要更換證書服務(wù)商,換上新的證書時,老的也就立即要失效,否則老的證書可能會被挪用到不法站點上;由于站點自身問題導(dǎo)致私鑰泄密,那么TLS的加密就不能保證安全;證書頒發(fā)機構(gòu)被冒名頂替等等。一旦發(fā)生以上任何一種情況,證書頒發(fā)者都要保證對應(yīng)證書立即失效。證書頒發(fā)者一般提供兩種方式來驗證證書有效性:CRL和OCSP。
CRL(Certificate Revocation List)即證書撤銷名單。證書頒發(fā)者會提供一份已失效證書的名單,供瀏覽器驗證證書使用。當(dāng)然這份名單是巨長無比的,瀏覽器不可能每次TLS都去下載,所以常用做法是瀏覽器會緩存這份名單,定期做后臺更新。這樣雖然后臺更新存在時間間隔,證書失效不實時,但一般也OK了。
OCSP(Online Certificate StatusProtocol)即在線證書狀態(tài)協(xié)議。除了離線文件,證書頒發(fā)者也會提供實時的查詢接口,查詢某個特定證書目前是否有效。實時查詢的問題在于瀏覽器需要等待這個查詢結(jié)束才能繼續(xù)TLS握手,延遲會很大。此外,通過『某個IP請求了某個證書的OCSP請求』這個信息,其實就暴露了某個用戶正在訪問某個網(wǎng)站了,也算是一種隱私的泄露。
以上是站在證書頒發(fā)者的角度說明會提供兩種判斷方式,實際情況下瀏覽器究竟會選擇哪種方式判斷,每個瀏覽器都有自己的實現(xiàn)。下圖是通過chrome查看某個網(wǎng)站的證書信息,可以看到其中的證書鏈和證書驗證信息。
中間人攻擊
介紹完證書如何保證站點的可信以后,我們來看看如果證書不合法會怎么樣。導(dǎo)致不合法證書還能被使用有兩種情況,一種是前文提到的CNNIC證書等類似的系統(tǒng)性風(fēng)險,另一種是用戶缺乏安全意識,手動信任了存在安全隱患的證書。
對于前者,我們要多關(guān)注安全新聞定期排雷,對于后者我們要提高安全意識,以后看到這些畫面時可要慎之又慎了,而不是一味地點同意。

一旦非法的證書被瀏覽器視為合法,那么就面臨著一種『中間人攻擊』的風(fēng)險:惡意節(jié)點會通過持有的非法證書和客戶端交互,之后代替客戶端,向真實服務(wù)器發(fā)起請求,并把服務(wù)器的請求返回給客戶端。這樣客戶端好像感覺自己直接連接了服務(wù)器,但實際上有個隱藏的中間人,神不知鬼不覺地竊取了中間的信息。
至此,已經(jīng)將TLS技術(shù)如何應(yīng)對『明文』和『無法驗證服務(wù)器的真實性』兩個主要安全問題講完了,由于本人并不從事網(wǎng)工相關(guān)職業(yè),因此就沒有深究到協(xié)議消息幀細(xì)節(jié),只是從原理層面進(jìn)行了理解。如果關(guān)于TLS還有什么沒有提到,或者理解不正確的,歡迎各位提出來~