TLS是如何保障數據傳輸安全(中間人攻擊)

前言

前段時間和同事討論HTTPS的工作原理,當時對這塊知識原理掌握還是靠以前看了一些部落格介紹,深度不夠,正好我這位同事是密碼學專業畢業的,結合他密碼學角度對tls加解密這闡述,讓我對這塊原理有了更進一步的理解,正文開始…

今天我們討論2個話題

  • TLS是如何保障數據傳輸安全的
  • 中間人攻擊的原理和攻防

什麼是TLS

TLS(Transport Layer Security)是新的標準,舊的標準叫SSL(Secure Sockets Layer)。
不管新舊標準,他們的目的都是同一個,那就是保護數據的安全,那…究竟是怎樣算保護呢?

TLS是如何保障數據傳輸安全的

先來說一般的情況,沒有SSL的時候,客戶端和伺服器,之間有一個傳輸通道是用來傳輸各種數據,但!!!這個通道是透明的,也就是說,其他人可以清楚的看到客戶端和伺服器到底在秘密的交換什麼東西,而有了SSL之後,就是從原本的透明傳輸通道升級成了非透明的傳輸通道。這樣其他人就不容易的看到到底在傳輸什麼東西了

image

這裡不就說http和https的概念了,簡單來說就是http有了ssl就升級為https。
http 就是走的透明的通道
https 就是非透明的通道

沒用https會經常被網路服務商植入廣告

沒準你也遇到過,訪問某個網站時會被植入廣告誘導點擊。

這就是有因為http是明文傳輸的,長城寬頻是知道你訪問的網站伺服器下發的明文,自然就可以在明文中加點「東西」
達到他不可告人的目的!!

image

長城寬頻還是比較收斂的,只會在第一次訪問植入廣告,後面再訪問就不會植入了。
反正這種行為是絕對鄙視的!!!

SSL是怎麼起作用的

既然明文傳輸是不行的,那麼把明文進行加密傳輸唄。

就是客戶端通過加密 傳輸到 服務端解密

反之

服務端加密 傳輸到 客戶端解密

在到數據加密傳輸這一步之前,得首先建立安全連接通道!

SSL需要用到非對稱加密的公私鑰達到認證並建立連接通道。

建立安全連接通道後,在利用對稱式加密對通道里所有的數據進行加解密。

聽起來有點繞,沒關係為了要了解整個概念,必須先了解下對稱式加密和非對稱式加密

對稱式加密

假如說有一個加密的演算法是【把字母往後移位k位,把位移後的結果以及k給對方】
所以當A想要和B說HI

A首先通過這個加密方法把HI這個詞,往後移2位,就變成了JK

當B收到位移數:2 以及JK。B就可以通過位移數2 往回推成HI

這裡的位移數:2 就是這個對稱式加密演算法的秘鑰

A和B都拿到同樣的秘鑰2,是一個對稱的概念!!

非對稱式加密

那就是A和B拿到的秘鑰是不同的。(上面的例子,A拿公鑰,B拿私鑰)

公鑰和私鑰一定是一組一對配對起來的。如果公鑰是O 私鑰是P 那麼絕對OP是一組。

B先把公鑰給A B手上有私鑰

  • 私鑰(是絕對不能外泄的) 公鑰是公開的

A 通過 B給的公鑰進行加密變成 xx ,B收到後用自己的私鑰把xx進行解密變成HI

如下圖:
image

整個SSL的建立的步驟分為3個大項目 ,粉色標記的三個大塊

  • Authentication 使用非對稱加密進行對伺服器下發的證書認證
  • Key Exchange (這裡注意當採用Diffie—Hellman演算法會和RSA演算法的不同點)
  • Encrypted Data Transfer 利用第二步的對稱式加密,對數據傳輸進行加解密

想要更詳細了解圖中每一步請參閱
那些關於ssl-tls的二三事-九

注意:在公開網路中比如瀏覽器訪問的站點,站點伺服器不會要求客戶端發送客戶端證書,所以上圖的4和5是灰色展示

下面說一下之前我誤解的點

1. RAS和DH

都知道SSL用到了非對稱加密,包括網上文章:
image

網上文章這麼說對於理解是有幫助的,如果你只想了解大概沒問題,但是作為程式設計師我們需要更加深入理解。

上面文章說的其實是針對採用RSA演算法的。因為DH演算法中不需要,所以不會在KeyExchange這一環節中利用非對稱加密傳輸數據!

DH演算法說白話一點,就是在教你如何「安全的」告訴對方密碼而不用擔心密碼被竊聽。
以下是擷取自wiki的DH流程簡圖:

image

上圖中,Alice和Bob通過DH演算法生成秘鑰K,
其中:

  • g、p是2個非私密數據;
  • a、b是私密數據;
  • A是根據:g、p、a算出來的非私密數據;B是根據:g、p、b算出來的非私密數據;
  • 把A從a傳到b,根據求K公式,b得到秘鑰k;a同理;

註:

  • p是一個大素數。p的位數決定了攻擊者破解的難度。
  • g則不需要很大,並且在一般的實踐中通常是2或者5。
2. RSA對稱加密演算法中的秘鑰(master secret)是不經過網路傳輸的
  • Pre-master secret (PMS)
  • Client』s random number
  • Server』s random number

PMS是通過網路傳輸的這裡面用到了非對稱加密!

3. 容易被忽略的重要點:客戶端驗證服務端證書也用到了非對稱加密

一般我們會給域名申請證書,最終的證書是最終用CA機構的Root根證書籤發的。

每一次簽發的證書,都是自己的私鑰做了簽名,並放在證書里。

由於CA機構也會用Root根證書籤發一些中間證書,再由中間證書籤發的證書去簽名生成你要申請的證書.

如下圖:

image

服務端下發也是下發一個證書鏈的結構,瀏覽器拿到後去一步步驗證。但光有證書鏈,客戶端是不能完成證書校驗的,必須有一張根證書才能迭代完成簽名認證,也就是說客戶端必須信任根證書才能構建信任基礎,那麼根證書在哪兒呢?

瀏覽器用的[可信任證書庫],

  • 在 windows 平台中,微軟有專門的根證書庫。
  • 在 Linux 平台中,軟體和服務一般使用 NSS 根證書庫。
  • 在蘋果平台中,也有專門的根證書庫。

在windows作業系統中 打開cmd 輸入 certmgr 就可以打開

image

瀏覽器在收到server端發來的ssl證書資訊,拿到證書後驗證其數字簽名。具體就是,根據證書上寫的CA簽發機構,在瀏覽器內置的根證書里找到對應的公鑰,用此公鑰解開數字簽名,得到摘要(digest,證書內容的hash值),據此驗證證書的合法性。

總之一句話,CA就是神一樣的存在,如果你信任神,你就應該信任這些CA。

中間人攻擊(MITM )

image
中間人攻擊,就是中間卡了一個人幫你和伺服器進行數據交換。

這樣就代表傳輸的所有數據都被這個中間人看光光。

為什麼我都用SSL了。不應該都是數據加密了嘛,中間人是如何知道的?

用Fiddler來模擬下中間人攻擊,要完成中間人攻擊需要配置

  • Fiddler會在證書信任中心安裝一個它的證書
  • 然後瀏覽器通過Fddler暴露的埠來訪問目標站點

image

首先看下正常的證書長啥樣的 如下圖:

image

中了中間人攻擊的證書是長啥樣的 ,如下圖

image

其實中間人的角色,就是充當了伺服器在和你建立SSL。

所以對瀏覽器來說,這個中間人就是真正的伺服器,只是瀏覽器不知情而已。

但是其實瀏覽器並不會那麼笨,因為如上面我們講了瀏覽器會去【可信任證書中心】去驗證。如果遇到不存在的證書,瀏覽器就會出現以下的提示

image

客戶端本身就是壞人的情況下,才會在自己的【可信任證書中心】裝一個用於中間人攻擊的證書。

有什麼辦法可以防止中間人攻擊 -》 SSL Pinning

什麼是SSL Pinning

SSL Pinning 也叫 Certificate Pinning

而前面有提到一個概念,公鑰私鑰是一對一配對的。

所以同一組公私鑰出來的憑證,這個憑證裡面的公鑰絕對是不會變的。

而 SSL Pinning 就是要把 SSL 固定起來,這個固定就是利用公鑰的特性實現的。

假設有一個APP是專門訪問baidu.com的

baidu.com證書裡面的公鑰是O的話,而我app裡面的程式碼,已經有預先寫好O這個公鑰,

所以當我的app瀏覽 baidu.com的時候,取得證書裡面的公鑰O

拿這個公鑰O和程式碼寫的O對比是否一致。如果不一致就拒絕。

為啥是中間人攻擊的話,那麼這2個O肯定不一致!

驗證一下就知道了,下面這段腳本可以從服務端下發的證書拿到公鑰

分別測試下 正常情況和中間人攻擊的情況

正常情況

#!/bin/bash
certs=`openssl s_client -connect $1:443 -servername $1 -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/Server certificate/p'`
rest=$certs
while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]
do
    cert="${rest%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
    rest=${rest#*-----END CERTIFICATE-----}
    echo `echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/'`
    echo "$cert" | openssl x509 -pubkey -noout | 
        openssl rsa -pubin -outform der 2>/dev/null | 
        openssl dgst -sha256 -binary | openssl enc -base64
done

百度的CA證書是一個證書鏈,如下圖,分別是


//頒發者是  GlobalSign Organization Validation CA
9ncsiOH9INfRO1dZosXOLZck/Z+/ikYsRl0e+iOUmiw=

//頒發者是 DigiCert Inc
BbkOPUFIMuqBj5SBjChDvpb1ZCdk3b9ZNDWOnKRB/bo=

image

中間人攻擊情況

需要設置 -proxy 去模擬


#!/bin/bash
certs=`openssl s_client -proxy 127.0.0.1:8888 -connect $1:443 -servername $1 -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/Server certificate/p'`
rest=$certs
while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]
do
    cert="${rest%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
    rest=${rest#*-----END CERTIFICATE-----}
    echo `echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/'`
    echo "$cert" | openssl x509 -pubkey -noout | 
        openssl rsa -pubin -outform der 2>/dev/null | 
        openssl dgst -sha256 -binary | openssl enc -base64
done

image

客戶端如何用程式碼去實現SSL Pinning


CertificatePinner certPinner = new CertificatePinner.Builder()
        .add("baidu.com",
              "sha256/9ncsiOH9INfRO1dZosXOLZck/Z+/ikYsRl0e+iOUmiw=")
        .build();

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .certificatePinner(certPinner)
        .build();