作者: 張洋
日期:2019-10
原創(chuàng)文章,轉(zhuǎn)載請注明出處
題目本來是在公司中做的一次分享,后來覺得30分鐘通過PPT能講的內(nèi)容實在有限,故整理成了這篇文章。
帶著數(shù)據(jù)去旅行
一次完整的HTTP請求
在講HTTPS之前,我們有必要了解一下HTTP協(xié)議。先看一個完整的HTTP請求:

上面是我用Wireshark工具所抓到的一次完整HTTP請求的樣子。
- 首先可以看到的是三個TCP協(xié)議的數(shù)據(jù)包,也就是我們常說的TCP三次握手,在這之后,兩臺電腦之間的連接就建立起來了。
- 繼續(xù)向下就可以看到HTTP請求了,Request和Response一來一回,很好辨認。大家還可以注意到在每個HTTP數(shù)據(jù)包之后還跟了一個TCP的包,這是TCP協(xié)議的確認,表示告訴發(fā)送者:"你剛才的報文我已經(jīng)收到了"。
頁面底部顯示了一個原始的HTTP GET請求報文的樣子,為什么請前面會出現(xiàn)亂碼呢?其實前面的亂碼就是TCP/IP協(xié)議。不同于HTTP協(xié)議可以用字符編碼,TCP/IP的傳遞需要極致壓縮數(shù)據(jù)量,每一個數(shù)據(jù)位就表示了一種特定的意思,需要使用TCP/IP的規(guī)則去解析(就像我們在軟件上面頁面所看到的解析結(jié)果),而不能使用字符編碼來閱讀。
什么是分層網(wǎng)絡模型
互聯(lián)網(wǎng)發(fā)展早期,需要解決的一個重要問題就是電腦之間如何傳輸數(shù)據(jù)。人們需要找到一種大家都認同的規(guī)范,每個想要相互連接的計算機都需要按照規(guī)范來辦事。這期間有一個協(xié)議脫穎而出,他就是現(xiàn)在應用非常廣泛的TCP/IP協(xié)議,它規(guī)定了網(wǎng)絡上的計算機要如何找到另一臺計算機,并且如何與之建立連接。TCP/IP的發(fā)明者還非常有遠見地提出了“網(wǎng)絡分層”的概念,將復雜的網(wǎng)絡通信劃分成了多個層次,每個層次只需要關(guān)心自己的問題,將大問題劃分為了很多個小問題從而解決了網(wǎng)絡通信的難題。就像我們寫代碼需要分層一樣(Service,Controller,Dao層等等),每一層只需要關(guān)注自己的功能。
值得注意的是,TCP/IP協(xié)議只有四層,而我們現(xiàn)在經(jīng)常說起的OSI七層網(wǎng)絡模型,是由國際標準組織(ISO)后來才提出來的。在此之前,雖然TCP/IP協(xié)議非常優(yōu)秀,但是奈何市面上還是各玩各的,所以ISO想要制定一個大一統(tǒng)的協(xié)議,在很多細節(jié)上進行了細化,成就了我們現(xiàn)在所看到的七層網(wǎng)絡模型。我們簡單對比一下OSI七層模型和TCP/IP的四層模型:

雖然OSI七層模型有更細分的設計,但是他也無法撼動TCP/IP已經(jīng)實行多年的統(tǒng)治地位,所以OSI模型現(xiàn)在更多是一種建議和標準。
在TCP/IP模型中,我們的HTTP協(xié)議,TCP協(xié)議,IP協(xié)議分別位于應用層,傳輸層和互聯(lián)網(wǎng)層。當我們發(fā)起一次HTTP請求,瀏覽器會按照HTTP協(xié)議的要求,拼裝出HTTP的數(shù)據(jù),再調(diào)用下層TCP協(xié)議提供的API將HTTP數(shù)據(jù)封裝到TCP協(xié)議中,再往下針對IP協(xié)議再一次封裝,最后到達物理網(wǎng)絡層,數(shù)據(jù)被發(fā)送出去。
目標主機收到數(shù)據(jù)以后,反過來將包裝好的數(shù)據(jù)一層一層剝開,最終得到我們要傳遞的信息,然后再一次將要返回的數(shù)據(jù)經(jīng)過層層協(xié)議的打包返回到源主機。
帶著保險箱去旅行
從上面的基礎知識我們不難發(fā)現(xiàn),HTTP協(xié)議是非常簡單且高效。
- HTTP直接運行在TCP協(xié)議之上,依靠TCP來實現(xiàn)自己的穩(wěn)定性。
- HTTP使用明文傳輸,數(shù)據(jù)可讀性和可擴展性都非常好。
但是它的簡單高效也成為了一把雙刃劍,原始的HTTP請求就像是在網(wǎng)上裸奔,所有信息都是明文傳遞,毫無安全可言。隨著科技的發(fā)展,人們對安全、隱私的要求越來越高,特別是在現(xiàn)在萬物互聯(lián)的時代,敏感信息如果不經(jīng)過保護直接在網(wǎng)上傳播,將造成極大的隱患。
------在這樣的歷史浪潮中,HTTPs應運而生。
HTTPs與HTTP的區(qū)別就在這個s上,它代表的是"TLS",既安全傳輸層協(xié)議。TLS也是運行在TCP之上的應用層協(xié)議,但是與一般傳輸數(shù)據(jù)的協(xié)議不同,TLS所關(guān)心的,只有安全。我們可以認為TLS就是一個運行在TCP上的保險箱,現(xiàn)在我們將需要保護的數(shù)據(jù)先裝入這個保險箱,再經(jīng)過TCP發(fā)送出去,就能實現(xiàn)我們對于安全的需求。我們將HTTP over TCP,變成了HTTP over TLS over TCP。
那么這個保險箱是如何工作的呢?這就是我們今天的重點。
何謂安全
我們考慮一下兩個人寫信的場景,怎樣才能做到安全的遠程交流呢?
- 我不想我們寫信的內(nèi)容被人看懂
- 得想個辦法保證內(nèi)容不被人修改
- 你得在信里面證明你的身份
- 一旦證明了這是你寫的,以后你可不許抵賴
如果能夠做到這四點,那么我們的信應該是比較安全的,而這四點正好對應了我們信息安全領域中的四個要點,既:
- 機密性
- 完整性
- 身份認證
- 不可否認
下面我們就逐一來分析一下HTTPs是如何做到這四點的:
1. 機密性
我們知道兩臺計算機之間要想傳輸數(shù)據(jù),這些數(shù)據(jù)必然會經(jīng)過層層路由,這些節(jié)點上的每臺計算機理論上都能看到我們的數(shù)據(jù),所以安全的首要目標就是對我們想要傳輸?shù)臄?shù)據(jù)進行加密。
根據(jù)密碼學的知識,擺在我們面前的有兩種選擇---對稱加密和非對稱加密。
對稱加密使用一把密鑰,加密解密都用它,在雙方都知道密鑰并且妥善保管的情況下理論上非常安全。但是對于我們復雜的網(wǎng)絡環(huán)境,一臺服務器需要服務眾多的客戶,難道要為每個客戶都配置一個對稱加密的密鑰嗎?這顯然不現(xiàn)實。

那么非對稱加密呢?一把私鑰對應一把公鑰,私鑰加密的內(nèi)容只有公鑰可以解開,公鑰加密的內(nèi)容也只有私鑰可以解開,那么我們在服務器上配置一把私鑰,再把公鑰發(fā)給所有客戶,是不是就行了呢?

答案是否定的,原因有兩點:
- 服務器是面向大眾客戶的,公鑰必然會公開,誰都可以獲取,那么路由的中間節(jié)點一樣可以拿著這把公鑰解析服務器返回的報文。
- 非對稱加密的性能無法適應互聯(lián)網(wǎng)高并發(fā)的環(huán)境,所有請求都用非對稱加密會導致連接異常緩慢,無法使用。
基于上述兩點,HTTPs做了一個非常聰明的選擇,就是混合加密?,F(xiàn)在我們在TCP三次握手的基礎上再引入TLS的四次握手,而這次會晤的重要目標就是協(xié)商出密鑰。為了便于理解我們先看一個簡單的模型:
- 客戶端通過可靠的渠道獲取到服務器的公鑰。
- 客戶端生成一個隨機數(shù)作為對稱加密的密鑰,也就是本次會話的主密鑰
(實際上生成主密鑰需要Server-Client都參與,通過相同的算法算出來,但是在老版本的RSA交換密鑰中最終的安全性還是客戶端保證的,所以這里我們簡單認為是客戶端生成了一個隨機數(shù)即可,后面會詳細講到) - 客戶端用Server的公鑰將主密鑰加密傳輸給Server。
-
后續(xù)交換內(nèi)容均用主密鑰進行加密解密。
混合加密.png
2. 完整性
機密性我們可以滿足之后,來看看第二個需要考慮的重點,完整性。
大家應該也有注意到,我們在HTTPs握手模型中的前幾次數(shù)據(jù)交換也都是明文傳輸?shù)?,因為雙方還沒有協(xié)商出主密鑰的時候是無法進行加密傳輸?shù)?。而在這個過程中又有許多關(guān)鍵的信息,這些信息我們需要有一定的手段保證他們不會被人修改。
這時候就需要引入數(shù)字簽名。
在數(shù)字簽名中首先要提到的就是摘要算法(也就是我們常說的Hash函數(shù),HTTPs中常見的有SHA-2族等),它的作用是將任意長度的數(shù)據(jù)映射成一串固定長度的字符串。摘要算法有幾個特點:
- 單向性,無法通過計算出來的字符串反推出原文
- 雪崩效應,即原文中一點點微小的改變也會導致計算出的字符串完全不一樣,無法通過規(guī)律逆向推導。
- 抵抗沖突,既然是將無限打的數(shù)據(jù)集映射到有限大的空間中(字符串空間),勢必會出現(xiàn)映射沖突的可能,既不同的數(shù)據(jù)映射出的字符串是相同的。抵抗沖突是摘要算法的一個重要指標,一個好的摘要算法他的沖突概率應該是非常非常小的。
這三個特點使得我們可以很好的驗證原文數(shù)據(jù)的完整性,我們只需要使用SHA-2計算出報文的摘要,將它附在報文后面。接收方收到報文后,再通過同樣的算法計算報文的摘要,如果和發(fā)送過來的摘要相同,那么就可以驗證
數(shù)據(jù)的完整性。
3. 身份認證
有了完整性驗證還是不夠,我們網(wǎng)絡中的人可能太壞了,如果他連摘要也一起修改了呢?這時候還需要為摘要上一把小小的鎖,不能讓人輕易修改,這就是數(shù)字簽名。
現(xiàn)在我們將摘要信息用RSA非對稱加密中的私鑰進行加密,在接收方使用公鑰解密驗簽,即便中間人想要修改摘要,但是他無法得知原始的私鑰,所以數(shù)字簽名是無法偽造的。
客戶端接收到請求之后用服務器的公鑰將簽名解密,再驗證完整性,這樣既可以保證消息沒有被篡改過,又可以保證消息確實是目標服務器發(fā)送的。

4. 不可否認
由于存在偽裝(任何一方都可能被中間人冒名頂替),所以其中一方可以假裝沒有收到對方消息或?qū)Ψ绞盏降南⒉皇亲约喊l(fā)的。
在使用數(shù)字簽名驗簽的過程中我們也實現(xiàn)了不可否認的特性,大家可以想一想,既然中間人無法獲取私鑰,也就無法偽造簽名。反過來講,一旦我們驗簽成功,說明數(shù)字簽名合法,那么上述信息就確確實實是對應的人發(fā)送的,無法抵賴。
5. 公鑰的信任問題
有了上述幾點的分析,我們?yōu)镠TTP設計的安全方案似乎已經(jīng)可以了。但是還有一個致命的問題,就怎么把公鑰安全地交到用戶手上?
之前的分析都基于一個假設,無論是非對稱交換密鑰,還是簽名的驗簽,都需要用到服務器的公鑰,并且我們都認為這把公鑰一定是和服務器的私鑰對應的。但是問題就出在這里,服務器如何安全的把公鑰傳遞給用戶?如果在公鑰的傳遞過程中就被人替換,那后續(xù)的加密等步驟不就沒有意義了嗎。
這個時候有同學會說,簡單啊,我們把這個公鑰加密一下傳輸給客戶不久好了嗎?
。。。。。。
等等,那加密公鑰用的密鑰又怎么傳輸?這已經(jīng)成了一個雞生蛋的問題,網(wǎng)絡協(xié)議的制定者們也意識到了這一點,光靠算法等技術(shù)手段已經(jīng)沒法了,一定要人為介入打破這個循環(huán)。
咣的一聲,CA(Certification Authority)閃亮登場。
CA介入之后,它以自己的信譽為各服務器的公鑰盡心擔保。這時候我們服務器在第一步發(fā)送給客戶的不再是簡單的公鑰,而是一連串的信息,包括服務器的域名,服務器持有者等等,當然最重要的還是服務器的公鑰。
這些信息并不是簡單的發(fā)送,他們經(jīng)過了CA的認證,CA使用自己的私鑰對他們進行了簽名,生成了數(shù)字證書。
然后CA通知微軟蘋果安卓。。。等終端操作系統(tǒng)提供商:“你們把我(CA)的公鑰先放在你們的操作系統(tǒng)里”。這就是我們常說的根證書。
這時候CA的擔保工作算是完成了??蛻舳嗽谑盏椒掌鞯?strong>數(shù)字證書之后,使用內(nèi)置在設備中的根證書進行驗簽,取得可信任的服務器公鑰,從而解決了公鑰的信任問題。

HTTPS握手過程
有了上述四點的分析,我們詳細看一下HTTPS的握手過程。前面我們有提到,HTTPs協(xié)議是在HTTP和TCP之間添加了一層TLS協(xié)議,它也屬于應用層協(xié)議,它的作用就是在TCP建立起連接之后對傳輸通道進行加密,將HTTP放在TLS之上,可以保證在不影響HTTP本身協(xié)議的情況下獲得加密傳輸?shù)奶匦?,所以HTTPS的握手過程也就是TLS的握手過程。


抓包數(shù)據(jù)顯示了一個完整的TLS握手過程(RSA密鑰交換),從圖中我們可以看到,直到第五個包,才開始正真?zhèn)鬏敂?shù)據(jù)Application Data,并且從第二張圖可以看出實際的Application Data(也就是HTTP協(xié)議內(nèi)容)已經(jīng)經(jīng)過了加密,抓包所能看到的也只是亂碼。
下面我們就詳細分析一下在正式傳送數(shù)據(jù)之前的四個步驟是怎么做的:

第一步,Client Hello。
首先由客戶端發(fā)起了一個打招呼的包:ClientHello,通過抓包我們可以看到TLS層的內(nèi)容:

有兩個點我們需要關(guān)注的就是Client傳輸了一個隨機數(shù)和21個Cipher Suites,我們詳細解釋一下這兩個點:
- Client Random:客戶端隨機數(shù)。在之前的原理中我們有講到HTTPs握手的主要目的就是得出對稱加密的主密鑰。這個密鑰單獨由某一方生成都不太合適,應為并不是每個主機都能產(chǎn)生完全的隨機數(shù),如果只是由某一方生成,很可能會產(chǎn)生比較弱的隨機數(shù),容易被猜測,導致加密密鑰被破解。所以我們在過程中會涉及到三個隨機數(shù),并且后續(xù)可以看到Server端也會參與到隨機數(shù)的生成,從而使“隨機數(shù)”更接近真實的隨機,保證安全。
- Cipher Suites:在之前的知識鋪墊中我們知道HTTPS的加密過程需要用到的密碼技術(shù)主要有:
- 對稱加密
- 非對稱加密
- 摘要算法
- 數(shù)字簽名
每一種算法分類中有很多的選擇,例如對稱加密就有AES_256_GCM, AES_128_CBC等,非對稱加密也有RAS和ECDSA,他們的安全強度和性能都各有側(cè)重。那在后續(xù)的連接中,針對算法的多種選擇,本次會話使用哪種組合呢?這些算法的組合就是我們說的Cipher Suites------密碼套件,看一個具體的Cipher Suite:
Cipher Suite
客戶端提供了許多這樣的組合,表示客戶端支持這些組合,供服務端進行選擇,每種組合的安全和性能各有差異,服務器結(jié)合自身情況盡心選擇。
第二步,Server Hello
服務器收到了客戶端的招呼請求之后,做出了Server Hello的回應,這里面有三個很重要的內(nèi)容:
- Server端隨機數(shù)
- 從Client Hello發(fā)送的Cipher Suites中選擇一個自己支持的Cipher Suite
- 服務器自己的CA證書
這一步服務器提供了必要的信息給客戶端,證書用于驗證自己的身份,以便確保公鑰安全的交到用戶手中,并且提供自己生成的隨機數(shù),和選定的Cipher Suite,讓客戶端繼續(xù)進行后續(xù)步驟。
第三步,Client Key Exchange
前兩個打招呼的報文實際上都是明文傳輸?shù)?,因為在建立連接之前雙方都沒有密鑰,無法使用加密傳輸?shù)?。而前文有提到的兩個隨機數(shù)(Client Random和Server Random)都有被截獲的風險,如果中間人獲取到我們的隨機數(shù),再根據(jù)同樣的算法進行計算,不是就可以得到我們最終生成的對稱密鑰了嗎?顯然我們不可能讓壞人輕易得逞,原因就在于第三步的Client Key Exchange中的第三個隨機數(shù),我們通常稱之為預主密鑰(Pre Master)。
客戶端在收到了服務器返回的消息之后,首先會用存放在本地的根證書對服務器證書進行驗證,驗證通過便會認為服務器證書中的公鑰是可信的,取出來備用。
接下來客戶端會生成第三個隨機數(shù),為了把這第三個隨機數(shù)安全的交給服務器,我們需要對他進行加密,這時的情況跟第一次Client Hello時有所不同,因為我們已經(jīng)有了可以信任的服務器公鑰。
客戶端根據(jù)服務器返回的Cipher Suite使用確定的算法和公鑰對第三個隨機數(shù)進行加密傳輸。這時候只有服務器的私鑰可以解密獲取這第三個隨機數(shù),從而保證了主密鑰的安全。
在這一步里,客戶端還做了一些小動作,由于客戶端現(xiàn)在已經(jīng)搶先服務端計算出了主密鑰,而當這條Client Key Exchange信息到達服務端時,服務端應該也能正確的計算出主密鑰,所以客戶端先用主密鑰對之前握手的信息做了摘要和簽名以供服務端計算出主密鑰之后當場進行驗證,這是雙方第一次使用對稱加密的主密鑰進行加密。
第四步,Server Change Cipher Spec
走到這一步,服務端會先用自己的私鑰解密,獲取到客戶端傳來的第三個隨機數(shù)(預主密鑰),接著通過相同的算法計算出主密鑰。服務端對上一步客戶端做的小動作進行驗簽,這是服務端第一次使用對稱加密主密鑰,如果驗簽成功說明我們雙方的主密鑰計算成功,是安全的。
接著服務端做了最后的回應:
- Change Cipher Spec,告訴客戶端,我準備好了,后續(xù)咱們就用主密鑰加密溝通,你懂的。
- 對我們握手的信息用主密鑰進行簽名,你驗證一下。
至此,整個HTTPs的握手完成。
后記
關(guān)于HTTPs的內(nèi)容確實有很多要講。比如本文所講的主要時原始的RSA密鑰交換,而目前應用最多的密鑰交換算法是ECDHE,它在交換隨機數(shù)與預主密鑰時使用了不同的策略,但是流程原理與RSA密鑰交換是共通的。
HTTPs的每一步都有它的用意,少了任何一步都會導致信息安全出現(xiàn)漏洞,原PPT的后面還有一部分反向案例分析,由于篇幅問題就下次再更新吧。

