引言
為什麼把這個作為選題。
大概也是2年前,我的同事,在面試某大廠遇到的問題與我一起探討。這個時候我發現,雖然TLS(https)這個東西大部分時候可能不會被直接用到,但很容易被作為考察的目標範圍。同時這方面的認知不同的人很容易出現不同的偏差。
問題內容概括來說就是URI/URL部分在https中是否會被加密。面試官認為url在https裡是不會被加密的,這與事實是有偏差的(最開始只是知道他這裡是有誤的,沒有思考這個地方之所以容易被誤解的原因),後面會提到,為什麼這個點容易出現理解偏差。
下文將著重分析說明HTTPS的安全性是如何被保證的。(本文會盡量避免直接引用名詞含義解釋,在說明過程中會重點描述協議與當前內容有直接相關的特性,部分內容會引用作者之前寫的帖子或評論)
HTTPS用什麼來保障傳輸安全
結論還是很簡單的:我們說https的安全性是由TLS保障的
先問是不是,再問為什麼!
HTTP與TLS
這張圖展示了http各版本店基本資料傳輸結構。(該圖引至bagder)
從HTTP 1 開始http就在利用TLS確保其安全性(事實上http 0.9就開始在使用SSL)。無論HTTP如何升級,承載其安全特性的元件TLS的地位始終沒有被動搖。(從1995年TLS3 首次部署到現在已經超過20年)
值得一提的是作為最知名的傳輸層協議TCP,HTTP也計劃在HTTP3中將其替換,不過其依然沿用最新版本的TLS保障其安全 (舉這個例子並不是說TCP不優秀)
上圖時間線簡單展示了HTTP及SSL/TLS的關鍵時間線(http1.1 在1999年時有一次定稿,有的資料上會認為1999年是http1.1正式釋出的時間點)。不難發現2者的發展一直保持微妙的同步,當然這與web爆發發展有關係,http最知名的版本http1.1 及 現在TLS的基礎版本SSL 3 都出現在1996年前後,其影響也一直持續到了現在。
20年前的基礎
上圖是http各版本當前在web系統中的使用佔比,不難看出 http 1.1 至今仍然被廣泛使用,這個數字在2020年甚至還有50% (雖然統計中HTTP 2+ 超過了7成,但是並不代表這7成服務不支援HTTP 1.1,他們中大部分可以自動協商降級支援1.1)
而1996年被重新設計的SSL3.0 也一直延用至今(1999年釋出的TLS1.0與SSL3.0 修改不大,以至於後面有的地方認為他們是同一個版本)
PS :HTTP與TSL是完全2個獨立的協議,只是HTTPS利用TLS保證其安全性。
TLS其他應用範圍
當然不能因為http一直與tls繫結,我們就相信tls在安全方面的能力。 (畢竟大家平時在引入三方資源時都會先確認用“他”的人多不多,大部分會優先信任被廣泛使用的資源)
我們常用的SSH利用TLS保證連線的安全性,http的好朋友Websocket也在使用TLS,我們常見的VPN也在使用TLS確保我們的隱私安全。
這些基礎事實是大家對TLS的認可,同時也表明TLS在安全傳輸方面的事實地位。
HTTPS/TLS如何確保資料傳輸安全
那TLS是如何保證其安全的,為什麼其地位20多年都無法被撼動。
金鑰交換
資料如何安全傳輸,當然第一時間就能想到“資料加密”(不過資料的傳輸安全遠不僅如此,除了機密性,還需要有鑑別,資料完整性,和不可抵賴性,這些特性TLS都可以做到,不過不在本文討論範圍)
雖然簡單加密是不夠的,不過訊息加密確實一切的基礎。看起來並不難,伺服器與客戶端只需要有一個只有他們2方才知道的金鑰,如何用這個金鑰傳輸資料就可以了。
現在真正的問題來了,這個金鑰要怎麼才能做的只有客戶端與伺服器才知道,我們知道在網際網路上任何資料包的傳輸都需要經過許許多多路由器等網路裝置,其中任意一臺裝置都知道你的資料包裡是什麼內容,我們不可能通過網路直接把這個金鑰傳送給對方。
有一個方案其實很常見,金鑰離線分發。比如常見的U盾,口令卡,銀行系統用的分量金鑰卡等等都是用的類似離線的手段保證金鑰的安全。這種金鑰交換的方式的確是安全了不過代價太大,對於廣泛應用與網際網路的http顯然不合適。
公開金鑰演算法
不過幸好早在上個世紀70年年代就出現了公開的非對稱加密演算法(公開金鑰演算法,個人覺得這個名字更能體現這個金鑰演算法的特點),而且其安全性到現如今依然被證明有效(這也是為什麼1995就已經問世的SSL3 可以作為TLS的基礎一直延用至今的原因),藉助於公開金鑰演算法使得金鑰在客戶端於伺服器之間的安全傳播成為可能。
TLS支援的金鑰交換演算法有許多(比如RSA金鑰交換,Diffie-Hellman金鑰交換.......)。我們看下RAS密碼交換(因為他被廣泛應用,且相對簡單),看他是如何保證金鑰交換的安全性的。
先介紹非對稱加密演算法的一個重要特點,加密時需要用到一對金鑰(公鑰,私鑰),公鑰加密的資料只能通過私鑰解密,私鑰加密的資料只能通過公鑰解密。
- 通常我們說的對稱加密基本上都是簡單異或或異或的簡單變種操作,這正好是計算機擅長的,所以速度非常快。(一般金鑰實際上只是一種二進位制資料塊)
- 非對稱加密演算法是用的數學演算法,無論是加解密,還是金鑰對的生成消耗都比對稱加密慢很多。(金鑰實際上是一個數字,很大的數字)
金鑰如何交換
前面我們已經提到了工作金鑰是不可以直接在網路上傳播的,既然金鑰不能中網路中直接傳播,那我們能不能把金鑰加密後傳輸呢?非對稱加密演算法與這個需求完美契合。
服務端可以先將公鑰直接傳送給客戶端,然後客戶端自己生成一個隨機金鑰(TLS裡這裡交換的其實是預主金鑰),然後使用公開的公鑰加密這個金鑰傳送給伺服器,因為私鑰只有伺服器知道,所以只有伺服器才能解密這個金鑰。這樣一來就出現了一個只有客戶端和伺服器才知道的預主金鑰,然後通過這個預主金鑰可以生成主金鑰,再最終生成工作金鑰。
上圖簡單概括了預主金鑰傳輸過程(其實就是TLS握手的其中一部分)
中間人攻擊
看起來是不是很完美,就算金鑰交換過程中的資料被其他人知道也沒有關係,因為他們沒有私鑰,沒有辦法解密,不過還是有漏洞,你連線的網路裝置可不是隻能看你的資料,他還可以任意截斷修改你的原始報文,這就是中間人攻擊。
雖然客戶端可以使用公鑰加密預主金鑰,不過客戶端的公鑰是從哪裡來的呢,其實也還是通過網路由伺服器下發給客戶端的。那既然也是通過網路,在下發公鑰的時候工作金鑰可還沒有生成,這就是說公鑰可以被網路中的第三方截獲然後替換,客戶端以為收到的是伺服器的公鑰,而實際上收到的是中間人自己生成的公鑰(中間人知道與之對應的私鑰),這樣一來客戶端就會在毫不知情的情況下與中間人完成金鑰交換(TLS握手),然後中間人會代替客戶端與服務端互動,使用者及服務端都不知道有中間人的操作,資料就會完全暴露給攻擊者。
證書驗證
公鑰基礎設施
現在問題就變成了客戶端如何確認自己收到的公鑰不是任何攻擊者的證書。PKI(公鑰基礎設施)完美的解決了這個問題。
其實之前就有提到過當前最安全的手段其實就是金鑰離線分發,而整個web系統網站這麼多不太可能每個網站每個伺服器都來給網站離線下發金鑰,這個時候就需要一箇中間商,一個大家都認可的中間商CA中心(證書籤發機構)來幫我們完成服務端公鑰的驗證,雖然為每個網站離線分發公鑰不太可能,不過我們可以提前為客戶端離線分發受信任的根證書,利用這個根證書去驗證網站服務端發客戶端的公鑰,這就是前面提到的PKI。
CA中心會提前將自己的根證書加入到作業系統的根證書列表(系統安裝的時候就在那裡了),許多瀏覽器也有自己受信根證書列表,這些證書都是內建的,網路嗅探者無法汙染這些根證書。
那作業系統憑什麼又如何要信任這些根證書呢? 這個就很直白了,剛剛說了是“權威”機構的根證書,你可以不信任,不過基於PKI的證書校驗將無法運作,當然這些權威的證書頒發機構也也確實要能對得起他的權威。
現在客戶端已經有了離線的信任根證書,那他是如何認證網站發過來的公鑰呢(網站發過來實際上是證書,這個證書裡包含公鑰)
證書驗證過程
client會校驗證書的合法性,並根據驗證結果決定是否連線伺服器。
如上圖在瀏覽器中任意找一個https的網頁,伺服器在建立TLS連線前都會先將自己的公鑰證書發給客戶端,我們檢視其證書資訊。
從這裡面我們能看到證書包含以下內容:
(1) Validity也即有效期,有效期包含生效時間和失效時間,是一個時間區間;
(2) 公鑰資訊Subject Public Key Info,包括公鑰的加密演算法和公鑰內容;
(3) 指紋資訊,指紋用於驗證證書的完整性,也是證書校驗的關鍵,他確保證書沒有被修改過。 其原理就是在頒發證書時,頒發者根據指紋演算法(單向雜湊函式)(此處證書使用了SHA-1和SHA-256演算法 有多個指紋是為了相容老的客戶端)計算整個證書的hash指紋【證書內容hash值使用CA私鑰加密就是指紋】並和證書放在一起,client在開啟證書時,自己也根據指紋演算法計算一下證書的hash值,同時使用自己信任的根證書的公鑰解密hash指紋計算出原始hash,如果hash值不一致,則表明證書內容被篡改過;
(4) 證書的簽名Certificate Signature Value和Certificate Signature Algorithm,對證書籤名所使用的Hash演算法和Hash值;
(5) 簽發該證書的CA機構Issuer;
(6) 該證書是簽發給哪個組織/公司資訊Subject;
(7) 證書版本Version、證書序列號Serial Number以及Extensions擴充套件資訊等。
上圖即是證書指紋校驗的主要過程,不難看出Client校驗證書的核心其實是CA公鑰解密出原始指紋。那這個CA公鑰從哪裡來,這就是上文中提到的系統根證書。
這裡就有一個關鍵點證書的頒發,我們現在知道根證書裡的公鑰是用來解密網站公鑰證書的指紋的,為了確保安全系統會有一批自己信任的CA公鑰列表(根證書),作業系統會提前內建好這些CA的根證書。這些根證書為什麼可以驗證網站的證書呢 ,因為網址的證書也是這些CA機構簽發的。CA對應的一般是權威機構或組織,然後由這些權威機構頒發證書時使用他們自己的私鑰去簽名(為證書生成指紋)然後再頒發給網站,這樣就確保了只有權威機構頒發給各個網站的證書才會被客戶端校驗通過。
網站證書的頒發
順便我們看一下CA頒發給網站的證書具體是怎樣的,一般有2部分組成
公鑰證書:這個就是網站後面會公開傳送給客戶端的證書 (CA的私鑰只會用來給這個證書籤名,不關心證書裡的金鑰)
私鑰檔案:裡面只有一個私鑰(與公鑰證書裡公鑰是一對,用來在TLS握手只解密預主金鑰)
如上圖sipv4.com.key就是私鑰檔案,sipv4.com.cer就是公鑰證書 (如果看的細,會發現cer裡不止含有一個CERTIFICATE,其實他是一個證書鏈)
上圖簡單描述了證書如何在TLS裡發揮作用。有一點需要注意,CA再為網站頒發證書時會驗證申請者是網站的擁有者,這樣可以確保只有網站的擁有者才可能擁有合法的證書。這一通操作下來即使中間人用自己生成的公鑰證書來欺騙客戶端時,客戶端也能馬上反應過來從而中止連結。
現在我們再來看下攻擊者的處境,攻擊者如果偽裝成網站A,握手時攻擊者需要給使用者傳送公鑰證書,攻擊者有2個選擇。
- 傳送真正的證書(因為公鑰證書是公開的,任何人都可以方便的獲取),這樣雖然可以通過客戶端的證書校驗,不過攻擊者因為沒有私鑰,無法解密客戶端發過來的預主金鑰,握手會最終失敗。
- 傳送攻擊者自己生成的公鑰證書,這時雖然攻擊者有對應的私鑰,不過因為自己生成的證書沒有CA的簽名,無法通過客戶端的證書驗證,握手也會被終止。
這樣一來金鑰的交換,交換過程的安全就都得到了保障。
結束
url加密
現在回到開頭,為什麼有些同學會認為https不會加密URL。前面講到的在金鑰交換完成前資料流其實是明文,也就是說網站公鑰證書的傳輸是以明文的形式進行的,上面也提到了證書裡是包含Subject,而這個資訊裡往往有目標網站的域名,所以如果直接檢視未解密的https流量,往往是可以看到域名的明文資訊的,這個資訊很有可能讓人誤以為這是url資訊
為什麼不使用公鑰加密資料
還有一個問題也會經常會被提到,TLS為什麼不使用公鑰直接加密資料,這個問題也與開頭的問題一樣,很容易有認知上的偏差
看了很多被廣泛接受的答案都是“效能”問題,我很早之前也是這麼認為的,不過現在我知道“效能”問題相對於安全性上是否可行那就根本不是問題,不能使用公鑰加密是因為安全上根本不可行,會讓TLS形同虛設!
隨便開啟一個https網站的證書,不同電腦不同時間獲取的證書裡的公鑰其實都是一樣的,也就是說這個公鑰是公開的而且任何人都知道,那就不可能自己用公鑰去加解密。這樣伺服器返回的資料那不是任何人都能解密了
如上圖任何人都知道baidu的公鑰,任何人也都能解密baidu返回給客戶端的資料。
當然TLS的握手是個十分嚴謹及複雜的過程,他還有很多認證的功能在裡面,如果直接用公鑰加密資料了那就拋棄了握手裡面的很多功能,這裡就不展開了。(因為就上面提到的所有返回資料都能被任何人解密那就是致命問題了,相比之下其他的問題都是小問題)
關於非對稱加密演算法應該加密什麼資料
而不用公鑰加密資料本身這是個策略,不僅僅是TLS 這些,絕大部分加密系統都是使用的這個策略,因為非對稱加密加密的內容本質其實是數字(可以想象把1kb的資料轉換成數字 會是個什麼情景,當然他也不會允許你加密這麼大的資料,還要特殊處理),並且還是數學運算,計算機處理起來會很慢(很多銀行機構為了提高速度還會使用特殊的加密裝置加密機/卡這類東西)。可以看到非對稱演算法天生就不是為加密長度不可控的資料設計的,他往往用來加密長度有限的關鍵資料,比如資料的hash值。而DES、AES這種對稱演算法就不一樣,他們加密主要靠異或跟置換,大資料直接分組 ,這些操作計算機天生就很在行速度很快,他們對加密這些任意長度的資料流就很合適。
總結:
絕大部分加密系統都不會用非對稱演算法直接加解密資料(因為就不是為這種場景設計的)
而TLS就更不可能用公鑰加密資料(因為除了上面提到的設計上的不合理,這種操作還讓服務返回的資料失去了任何保護能力)
TLS地位為什麼難以被動搖
是不是有必要,作為其基礎的非對稱加密演算法目前為止依然十分十分可靠,而已也找不到更加合適的替代方案。
與其有直接關係的PKI體系已經形成,已經遍佈全球CA中心並不是這麼容易建立,因為需要大家互相信任,要推翻這個體系也並不容易。