一直困擾于HTTP和HTTPS的區別,現在專門找一個時間來抓住痛點,解決掉該麻煩。
1. HTTP + 加密 + 認證 + 完整性保護 = HTTPS
我們知道HTTP是明文傳輸的,就必不可免存在如下問題:
- 重要數據被明文獲取
- 通信雙方可能被偽冒
- 數據被篡改
一般獲取簡單數據用于展示的,可能無所謂以上的安全缺陷。但假如涉及類似銀行密碼的數據,就必須慎重考慮這一點了。
所以能夠規避以上缺陷的HTTP就是HTTPS(HTTP Secure)。
1.1 如何做到加密 + 認證 + 完整性保護
我們都知道OSI7層模型,其中HTTP屬于應用層協議,HTTP下一層是TCP(傳輸層協議)。完全性是一個難題,專注于傳輸速率的傳輸層協議TCP為了單一職責的理念,自是不會多管閑事去保證安全性而降低自身的傳輸速率的。
HTTP本身假如要去保證部分數據的安全性而去專注安全性的開發,也是得不償失。想到這里,很是有一些學java感受到的職責單一、職責分離這樣的思想,哈哈。所以應該就是這樣,HTTP與TCP之間再加上一層SSL/TLS(Secure Sockets Layer/Transport Layer Security)協議。
這樣就算是明白HTTP、HTTPS的區別了嗎?怎么可能呢?不去搞懂為啥就能安全了、為啥是SSL/TLS這兩個什么東西、為啥用TLS而不是SSL?不搞明白這些問題,怎么能安心呢?
2. SSL/TLS是什么
上一小節講到,SSL/TLS是HTTP和TCP之間的中轉協議。如圖,可知SSL/TLS也是一個應用層的協議。
那么我們可以把SSL/TLS當作一個黑盒子,就像TCP、HTTP,我們知道數據先丟給HTTP,HTTP丟給SSL/TLS加密,然后SSL/TLS丟給TCP傳輸...諸如此類,其中具體細節,暫先不表。
3. SSL和TLS的區別與歷史
Android 8.0禁用了SSL,所以我們一定非常好奇二者之間的區別,為什么會有兩種協議,而且總是并列提起。
SSL是TLS的前身,SSL從1.0、2.0到3.0一步步修訂,但安全性都不是非常完美。知道后來SSL3.0搖身一變變成TLS1.0,發展至今已經達到TLS1.2的穩定版本。另外18年今年,TLS1.3的草案也已經提出,在不斷開發中吧
- SSL 1.0,未發布公開,因為嚴重的安全性漏洞
-
SSL 2.0,1995.02,包含一些需要在SSL3.0解決的安全漏洞
SSL 2.0 在2011年被RFC 6176禁止 -
SSL 3.0,1996,代表著該協議的完整重新設計,
SSL 3.0也在后來June 2015被RFC 7568禁止. - TLS 1.0 在January 1999首次在RFC 2246中定義,作為SSL 3.0的升級版本
- TLS 1.1April 2006在RFC 4346 中定義
- TLS 1.2August 2008 在 RFC 5246定義,基于TLS 1.1進行升級
所有TLS版本在2011年3月rfc 6176中進一步完善,取消了它們對SSL的向下兼容,TLS再也不支持安全套接字層(SSL)2.0版。
TLS各個版本的更新情況可以看這里 - 維基百科
所以日常提起總是SSL/TLS,TLS是后來的所以放在后面。而維基百科是TLS/SSL,考慮的應該是TLS現在是主流吧。我們現在差不多也可以說成是TLS了,畢竟SSL已經被禁用了。
以下有幾張圖,可以比較直觀地說明,為什么用TLS,TLS相對于SSL有哪些優勢:
最后這一張太大了,截不完整,只能管中窺豹了。在此僅僅截下Android部分,畢竟是學習Android的。小伙伴們感興趣自己到維基百科查看。
4. TLS/SSL工作機制
4.1 加密方法
共享密鑰方式加密 = 對稱加密 = 處理速度較快
公開密鑰方式加密 = 非對稱加密 = 更復雜,處理速度較慢
共享密鑰方式加密只要密鑰ok就足夠安全,服務器只要把密鑰交給客戶端,然后通信過程中和客戶端使用同一把密鑰進行加密解密即可。畢竟是HTTP通信過程肯定是需要速度盡量快才是最好。
Q1:但是共享密鑰如何安全地遞交給對方?比如服務端如何把共享密鑰安全交給客戶端?
這時候就需要使用公開密鑰方式加密。想用密文和公鑰恢復到信息原文是異常困難的,相當于對離散對數進行求值,這不是輕而易舉能達到的。(還是沒有一個很好的概念,總之這很安全就對了)
發送方使用接收方的公鑰對數據進行加密,然后接收方收到密文后使用密鑰對數據進行解密。
接上一個問題:通信雙方持有對方的公鑰,發送共享密鑰時使用公鑰加密,就不怕共享密鑰被獲取了。
Q2:公鑰畢竟是要發放出去的,如何證明發給客戶端的過程中,公鑰沒有被替換掉呢?假如公鑰被替換掉,偽冒者就可以假裝成服務端和用戶進行通信。
接下來就是數字證書認證機構出場。
HTTPS中,服務端將公鑰發給數字證書認證機構進行安全認證并對公鑰進行數字簽名,完成后公鑰和簽名組合成數字證書。在和客戶端通信時,服務端將數字證書發給客戶端,客戶端通過第三方安全認證機構發布的公鑰(一般會在瀏覽器開發時,內置在瀏覽器中)對數字證書上的簽名進行驗證,假如驗證通過,則能證明以下事實:
- 認證該服務器的公鑰的機構是真實有效的數字證書認證機構
- 該服務器發過來的公鑰是值得信賴的
因為通信雙方都需要證明自己發出的公鑰真實可靠,所以也就存在兩種目的性不同的數字證書:可證明組織真實性的EV SSL證書、用以確認客戶端的客戶端證書
4.2 工作流程
TCP需要三次握手四次揮手,TLS/SSL也需要握手。
以上這些ClientHello都是報文的名稱,一個箭頭表示一次報文發送。
- ClientHello:包含支持的TLS最高版本、隨機數、加密組件(cipher suites)列表、支持的壓縮方法
- ServerHello:根據ClientHello選擇的TLS版本號、加密組件、壓縮方法,以及隨機數。假如連接需要從異常關閉中恢復,還會發送一個Session id。
- Certificate:包含公鑰證書。(該次報文也可能不發送,取決于加密組件的選擇)
- ServerKeyExchange:取決于加密組件,所有DHE 和 DH_anon 加密組件都會發送該報文。但google已經棄用DHE加密組件了,所以該報文在《圖解HTTP》一書中甚至沒有提及。
- ServerHelloDone:通知客戶端,最初階段的握手協商完成了。
- ClientKeyExchange:可能包含稱為PreMasterSecret的隨機密碼串、公鑰或者啥都不包含(取決于使用的加密組件),其中PreMasterSecret會使用Certificate報文中的數字證書中的公鑰進行加密。
- The client and server then use the random numbers and PreMasterSecret to compute a common secret, called the "master secret". All other key data for this connection is derived from this master secret (and the client- and server-generated random values), which is passed through a carefully designed pseudorandom function.
粗劣翻譯:客戶端和服務器使用隨機數和PreMasterSecret,通過精心設計的偽隨機函數來計算出一個共同的密鑰,稱為“master secret”。當前連接的所有其他密鑰公鑰數據都需由master secret(以及客戶端和服務器生成的隨機值)派生。
- ChangeCipherSpec:告訴服務端,從現在起所有我發的信息都會經過認證了!
- Finished:該報文起就已經是加密認證過的報文了,其中包含了握手至今全部報文的整體散列值hash和MAC(Message Authentication Code)。服務端會試圖解密該報文并驗證hash和MAC。如果驗證失敗,這次握手就失敗了,關閉。
- ChangeCipherSpec:告訴客戶端,從現在起所有我發的信息都會經過認證了!
- Finished:客戶端同樣去解密該報文并校驗。解密校驗失敗,一樣會關閉握手。
- HTTP request/response:接下來就可以進行HTTP的請求了。
- close_notify:最后由客戶端通知服務端進行關閉,發送close_notify報文。當然此時關閉的是TLS連接,繼續下一層TCP的四次揮手依舊會進行。
還有一個Client-authenticated TLS handshake,翻譯過來差不多是客戶端認證的握手流程,和基本流程差不大,就不重新寫一遍了,感興趣的同學去維基百科看一遍。
至此,總結一下:通過本文,了解了HTTP和HTTPS之間的區別、TLS/SSL的歷史發展、TLS/SSL的工作機制。差不多算是達到目的了。
可惜水平有限、時間有限,無法深入去追求把加密方面的內容深入了。
參考文獻: