上文說到HTTPS的三次握手:http://www.cnblogs.com/wh4am1/p/6616851.html
不懂的再回頭去看看
三、中間人攻擊
https握手過程的證書校驗環節就是為了識別證書的有效性唯一性等等,所以嚴格意義上來說https下不存在中間人攻擊,存在中間人攻擊的前提條件是沒有嚴格的對證書進行校驗,或者人為的信任偽造證書,下面一起看下幾種常見的https“中間人攻擊”場景。
1.證書未校驗
由於客戶端沒有做任何的證書校驗,所以此時隨意一張證書都可以進行中間人攻擊,可以使用burp里的這個模塊進行中間人攻擊。
通過瀏覽器查看實際的https證書,是一個自簽名的偽造證書。
2.部分校驗
做了部分校驗,例如在證書校驗過程中只做了證書域名是否匹配的校驗,可以使用burp的如下模塊生成任意域名的偽造證書進行中間人攻擊。
實際生成的證書效果,如果只做了域名、證書是否過期等校驗可輕松進行中間人攻擊(由於chrome是做了證書校驗的所以會提示證書不可信任)。
3.證書鏈校驗
如果客戶端對證書鏈做了校驗,那么攻擊難度就會上升一個層次,此時需要人為的信任偽造的證書或者安裝偽造的CA公鑰證書從而間接信任偽造的證書,可以使用burp的如下模塊進行中間人攻擊。
4.手機客戶端Https數據包抓取
上述第一、二種情況不多加贅述,第三種情況就是我們經常使用的抓手機應用https數據包的方法,即導入代理工具的公鑰證書到手機里,再進行https數據包的抓取。導入手機的公鑰證書在android平台上稱之為受信任的憑據,在ios平台上稱之為描述文件,可以通過openssl的命令直接查看我們導入到手機客戶端里的這個PortSwiggerCA.crt。
可以看見是Issuer和Subject一樣的自簽名CA公鑰證書,另外我們也可以通過證書類型就可以知道此為公鑰證書,crt、der格式的證書不支持存儲私鑰或證書路徑(有興趣的同學可查找證書相關信息)。導入CA公鑰證書之后,參考上文的證書校驗過程不難發現通過此方式能通過證書鏈校驗,從而形成中間人攻擊,客戶端使用代理工具的公鑰證書加密隨機數,代理工具使用私鑰解密並計算得到對稱加密密鑰,再對數據包進行解密即可抓取明文數據包。
5.中間人攻擊原理
一直在說中間人攻擊,那么中間人攻擊到底是怎么進行的呢,下面我們通過一個流行的MITM開源庫mitmproxy來分析中間人攻擊的原理。中間人攻擊的關鍵在於https握手過程的ClientKeyExchange,由於pre key交換的時候是使用服務器證書里的公鑰進行加密,如果用的偽造證書的公鑰,那么中間人就可以解開該密文得到pre_master_secret計算出用於對稱加密算法的master_key,從而獲取到客戶端發送的數據;然后中間人代理工具再使用其和服務端的master_key加密傳輸給服務端;同樣的服務器返回給客戶端的數據也是經過中間人解密再加密,於是完整的https中間人攻擊過程就形成了,一圖勝千言,來吧。
通過讀Mitmproxy的源碼發現mitmproxy生成偽造證書的函數如下:
通過上述函數一張完美偽造的證書就出現了,使用瀏覽器通過mitmproxy做代理看下實際偽造出來的證書。
可以看到實際的證書是由mimtproxy頒發的,其中的公鑰就是mimtproxy自己的公鑰,后續的加密數據就可以使用mimtproxy的私鑰進行解密了。如果導入了mitmproxy的公鑰證書到客戶端,那么該偽造的證書就可以完美的通過客戶端的證書校驗了。這就是平時為什么導入代理的CA證書到手機客戶端能抓取https的原因。
四、證書校驗
通過上文第一和第二部分的說明,相信大家已經對https有個大概的了解了,那么問題來了,怎樣才能防止這些“中間人攻擊”呢?
app證書校驗已經是一個老生常談的問題了,但是市場上還是有很多的app未做好證書校驗,有些只做了部分校驗,例如檢查證書域名是否匹配證書是否過期,更多數的是根本就不做校驗,於是就造成了中間人攻擊。做證書校驗需要做完全,只做一部分都會導致中間人攻擊,對於安全要求並不是特別高的app可使用如下校驗方式:
-
查看證書是否過期
-
服務器證書上的域名是否和服務器的實際域名相匹配
-
校驗證書鏈
可參考http://drops.wooyun.org/tips/3296,此類校驗方式雖然在導入CA公鑰證書到客戶端之后會造成中間人攻擊,但是攻擊門檻已相對較高,所以對於安全要求不是特別高的app可采用此方法進行防御。對於安全有較高要求一些app(例如金融)上述方法或許還未達到要求,那么此時可以使用如下更安全的校驗方式,將服務端證書打包放到app里,再建立https鏈接時使用本地證書和網絡下發證書進行一致性校驗。
此類校驗即便導入CA公鑰證書也無法進行中間人攻擊,但是相應的維護成本會相對升高,例如服務器證書過期,證書更換時如果app不升級就無法使用,那么可以改一下,生成一對RSA的公私鑰,公鑰可硬編碼在app,私鑰放服務器。 https握手前可通過服務器下發證書信息,例如公鑰、辦法機構、簽名等,該下發的信息使用服務器里的私鑰進行簽名; 通過app里預置的公鑰驗簽得到證書信息並存在內容中供后續使用; 發起https連接獲取服務器的證書,通過對比兩個證書信息是否一致進行證書校驗。
這樣即可避免強升的問題,但是問題又來了,這樣效率是不是低太多了?答案是肯定的,所以對於安全要求一般的應用使用第一種方法即可,對於一些安全要求較高的例如金融企業可選擇第二種方法。
說了挺多,但是該來的問題還是會來啊!現在的app一般采用混合開發,會使用很多webveiw直接加載html5頁面,上面的方法只解決了java層證書校驗的問題,並沒有涉及到webview里面的證書校驗,對於這種情況怎么辦呢?既然問題來了那么就一起說說解決方案,對於webview加載html5進行證書校驗的方法如下:
webview創建實例加載網頁時通過onPageStart方法返回url地址; 將返回的地址轉發到java層使用上述的證書校驗代碼進行進行校驗; 如果證書校驗出錯則使用stoploading()方法停止網頁加載,證書校驗通過則正常加載。










