HTTP協議發展至今已經有二十多年的歷史,整個發展的趨勢主要是兩個方向:效率和安全。效率方面,從HTTP1.0的一次請求一個鏈接,到HTTP1.1的鏈接複用,到SPDY/HTTP2的多路複用,到QUIC/HTTP3的基於UDP傳輸,在效率方面愈來愈高效。安全方面,從HTTP的明文,到HTTP2強制使用TLSv1.2,到QUIC/HTTP3強制使用TLSv1.3,愈來愈注重數據傳輸的安全性。總而言之,HTTP協議的發展對用戶是友好的,可是對開發者而言卻不那麼友善。php
抓包是每一個程序員的必修技能之一,尤爲是在接口調試和程序逆向方面具備廣闊的用途。可是,隨着愈來愈多的通訊協議使用加密的HTTPS,並且系統層面也開始強制規定使用HTTPS,抓包彷佛是顯得愈來愈難了。android
本篇博客,主要詳解Android平臺下,HTTPS抓包的常見問題以及解決辦法。工欲善其事必先利其器,博客中以HttpCanary做爲抓包工具進行講解。更多HttpCanary的資料,請見:github.com/MegatronKin…git
幾乎全部網絡數據的抓包都是採用中間人的方式(MITM),包括你們經常使用的Fiddler、Charles等知名抓包工具,HttpCanary一樣是使用中間人的方式進行抓包。程序員
從上面這個原理圖,能夠看出抓包的核心問題主要是兩個:github
第一個問題,MITM Server要成爲真正的Server,必須可以給指定域名簽發公鑰證書,且公鑰證書可以經過系統的安全校驗。好比Client發送了一條https://www.baidu.com/的網絡請求,MITM Server要假裝成百度的Server,必須持有www.baidu.com域名的公鑰證書併發給Client,同時還要有與公鑰相匹配的私鑰。後端
MITM Server的處理方式是從第一個SSL/TLS握手包Client Hello中提取出域名www.baidu.com,利用應用內置的CA證書建立www.baidu.com域名的公鑰證書和私鑰。建立的公鑰證書在SSL/TLS握手的過程當中發給Client,Client收到公鑰證書後會由系統會對此證書進行校驗,判斷是不是百度公司持有的證書,但很明顯這個證書是抓包工具僞造的。爲了可以讓系統校驗公鑰證書時認爲證書是真實有效的,咱們須要將抓包應用內置的CA證書手動安裝到系統中,做爲真正的證書發行商(CA),即洗白。這就是爲何,HTTPS抓包必定要先安裝CA證書。瀏覽器
第二個問題,MITM Client假裝成Client。因爲服務器並不會校驗Client(絕大部分狀況),因此這個問題通常不會存在。好比Server通常不會關心Client究竟是Chrome瀏覽器仍是IE瀏覽器,是Android App仍是iOS App。固然,Server也是能夠校驗Client的,這個後面分析。安全
抓包應用內置的CA證書要洗白,必須安裝到系統中。而Android系統將CA證書又分爲兩種:用戶CA證書和系統CA證書。顧明思議,用戶CA證書是由用戶自行安裝的,系統CA證書是由系統內置的,很明顯後者更加真實有效。服務器
系統CA證書存放在/etc/security/cacerts/目錄下,名稱是CA證書subjectDN的Md5值前四位移位取或,後綴名是.0,好比00673b5b.0。考慮到安全緣由,系統CA證書須要有Root權限才能進行添加和刪除。微信
對於非Root的Android設備,用戶只能安裝用戶CA證書。
不管是系統CA證書仍是用戶CA證書,均可以在設置->系統安全->加密與憑據->信任的憑據中查看:
Android從7.0開始系統再也不信任用戶CA證書(應用targetSdkVersion >= 24時生效,若是targetSdkVersion < 24即便系統是7.0+依然會信任)。也就是說即便安裝了用戶CA證書,在Android 7.0+的機器上,targetSdkVersion >= 24的應用的HTTPS包就抓不到了。
好比上面的例子,抓包工具用內置的CA證書,建立了www.baidu.com域名的公鑰證書發給Client,系統校驗此證書時發現是用戶CA證書籤發的,sorry。。。
那麼,咱們若是繞過這種限制呢?已知有如下四種方式(低於7.0的系統請忽略):
若是咱們想抓本身的App,只須要在AndroidManifest中配置networkSecurityConfig便可:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config" ... >
...
</application>
</manifest>
複製代碼
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
複製代碼
這樣即表示,App信任用戶CA證書,讓系統對用戶CA證書的校驗給予經過。更多相關信息,詳見Network security configuration。
若是想抓一個App的包,能夠找個歷史版本,只須要其targetSdkVersion < 24便可。然而,隨着GooglePlay開始限制targetSdkVersion,如今要求其必須>=26,2019年8月1往後必須>=28,國內應用市場也開始逐步響應這種限制。絕大多數App的targetSdkVersion都將大於24了,也就意味着抓HTTPS的包愈來愈難操做了。
若是咱們但願抓targetSdkVersion >= 24的應用的包,那又該怎麼辦呢?咱們可使用平行空間或者VirtualApp來曲線救國。平行空間和VirtualApp這種多開應用能夠做爲宿主系統來運行其它應用,若是平行空間和VirtualApp的targetSdkVersion < 24,那麼問題也就解決了。
在此,我推薦使用平行空間,相比部分開源的VirtualApp,平行空間運行得更加穩定。但必須注意平行空間的版本4.0.8625如下才是targetSdkVersion < 24,別安裝錯了。固然,HttpCanary的設置中是能夠直接安裝平行空間的。
對於Root的機器,這是最完美最佳的解決方案。若是把CA證書安裝到系統CA證書目錄中,那這個假CA證書就是真正洗白了,不是真的也是真的了。因爲系統CA證書格式都是特殊的.0格式,咱們必須將抓包工具內置的CA證書以這種格式導出,HttpCanary直接提供了這種導出選項。
操做路徑:設置 -> SSL證書設置 -> 導出HttpCanary根證書 -> System Trusted(.0)
PS. 很不幸的HttpCanary v2.8.0前導出的證書名稱可能不正確,建議升級到v2.8.0以上版本操做。
導出.0格式的證書後,可使用MT管理器將.0文件複製到/etc/security/cacerts/目錄下,或者經過adb remount而後push也可(這裏稍微提一下,別在sdcard裏找這個目錄)。
火狐瀏覽器Firefox自行搞了一套CA證書管理,不管是系統CA證書仍是用戶CA證書,Firefox統統都不承認。這種狀況,咱們須要將CA證書經過特殊方式導入到Firefox中,不然Firefox瀏覽網頁就沒法工做了。
HttpCanary v2.8.0版本提供了Firefox證書導入選項。在設置 -> SSL證書設置 -> 添加HttpCanary根證書至Firefox 中:
點擊右上角複製按鈕將url複製到粘貼板,而後保持此頁面不動,打開Firefox粘貼輸入複製的url。
出現下載證書彈框後,必定要手動勾上:信任用來標誌網站和信任用來標誌電子郵件用戶。而後肯定便可。
證書固定(Certificate Pinning)是指Client端內置Server端真正的公鑰證書。在HTTPS請求時,Server端發給客戶端的公鑰證書必須與Client端內置的公鑰證書一致,請求才會成功。
在這種狀況下,因爲MITM Server建立的公鑰證書和Client端內置的公鑰證書不一致,MITM Server就沒法假裝成真正的Server了。這時,抓包就表現爲App網絡錯誤。已知的知名應用,好比餓了麼,就採用了證書固定。
另外,有些服務器採用的自簽證書(證書不是由真正CA發行商簽發的),這種狀況App請求時必須使用證書固定。
證書固定的通常作法是,將公鑰證書(.crt或者.cer等格式)內置到App中,而後建立TrustManager時將公鑰證書加進去。不少應用還會將內置的公鑰證書假裝起來或者加密,防止逆向提取,好比餓了麼就假裝成了png,固然對公鑰證書假裝或者加密沒什麼太大必要,純粹自欺欺人罷了。
證書固定對抓包是個很是麻煩的阻礙,不過咱們老是有辦法繞過的,就是麻煩了點。
Xposed和Magisk都有相應的模塊,用來破解證書固定,實現正常抓包。
破解的原理大體是,Hook建立SSLContext等涉及TrustManager相關的方法,將固定的證書移除。
Xposed和Magisk須要刷機等特殊處理,可是若是不想刷機折騰,咱們還能夠在VirtualApp中加入Hook代碼,而後利用VirtualApp打開目標應用進行抓包。固然,有開發者已經實現了相關的功能。詳見:
不過,這裏CertUnpinning插件的代碼有點問題,要改改。
若是Client固定了公鑰證書,那麼MITM Server必須持有真正的公鑰證書和匹配的私鑰。若是開發者具備真正服務端的公鑰證書和私鑰,(好比百度的公鑰證書和私鑰百度的後端開發確定有),若是真有的話,能夠將其導入HttpCanary中,也能夠完成正常抓包。
在設置 -> SSL證書設置 -> 管理SSL導入證書 中,切換到服務端,而後導入公鑰證書+私鑰,支持.p12和.bks格式文件。
SSL/TLS協議提供了雙向認證的功能,即除了Client須要校驗Server的真實性,Server也須要校驗Client的真實性。這種狀況,通常比較少,可是仍是有部分應用是開啓了雙向認證的。好比匿名社交應用Soul部分接口就使用了雙向認證。使用了雙向認證的HTTPS請求,一樣沒法直接抓包。
關於雙向認證的原理。
首先,雙向認證須要Server支持,Client必須內置一套公鑰證書 + 私鑰。在SSL/TLS握手過程當中,Server端會向Client端請求證書,Client端必須將內置的公鑰證書發給Server,Server驗證公鑰證書的真實性。
注意,這裏的內置的公鑰證書有區別於前面第5點的公鑰證書固定,雙向認證內置的公鑰證書+私鑰是額外的一套,不一樣於證書固定內置的公鑰證書。
若是一個Client既使用證書固定,又使用雙向認證,那麼Client端應該內置一套公鑰證書 + 一套公鑰證書和私鑰。第一套與Server端的公鑰證書相同,用於Client端系統校驗與Server發來的證書是否相同,即證書固定;第二套SSL/TLS握手時公鑰證書發給Server端,Server端進行簽名校驗,即雙向認證。
用於雙向認證的公鑰證書和私鑰表明了Client端身份,因此其是隱祕的,通常都是用.p12或者.bks文件+密鑰進行存放。因爲是內置在Client中,存儲的密鑰通常也是寫死在Client代碼中,有些App爲了防反編譯會將密鑰寫到so庫中,好比S匿名社交App,可是隻要存在於Client端中都是有辦法提取出來的。
這裏以S匿名社交App爲例,講解下如何抓取使用了雙向認證的App的HTTPS包。
若是服務器使用了Nginx且開啓了雙向認證,抓包時會出現400 Bad Request的錯誤,以下:
有些服務器可能不會返回404,直接請求失敗。
接下來看,如何使用HttpCanary配置雙向認證抓包。
首先,解壓APK,提取出.p12或者.bks文件,二進制的文件通常存放都在raw或者assets目錄。
將client.p12文件導入手機,而後在HttpCanary的設置 -> SSL證書設置 -> 管理SSL導入證書中,切換到客戶端(由於須要配給MITM Client),而後導入.p12文件。
因爲雙向認證的公鑰證書和私鑰是受密鑰保護的,因此須要輸入密碼:
通常經過逆向能夠從APK中提取出密鑰,具體操做這裏略過。輸入密鑰後,須要輸入映射域名,這裏使用通配符*映射全部相關域名:
導入完成後以下:
能夠點進證書詳情查看細節,這個client.p12文件包含公鑰證書和私鑰,是用於雙向認證的。
配置完成後,從新進行抓包,看看效果。
能夠看到,以前400 Bad Request的兩個要求雙向認證的請求成功了!
有些服務器可能會開啓SSL重協商,即SSL/TLS握手成功後發送請求時服務器會要求從新握手。這種狀況通常比較少,可是也不排除,已知的應用好比 10000社區 就使用了SSL重協商。
因爲Android系統對SSL重協商是有限支持,因此部分系統版本抓包會失敗,表現爲網絡異常。在Android 8.1如下,SslSocket是徹底支持SSL重協商的,可是SSLEngine倒是不支持SSL重協商的,而HttpCanary解析SSL/TLS使用的是SSLEngine。在Android 8.1及以上,SSLEngine和SslSocket統一了實現,故是支持SSL重協商的。
因此,若是確認服務器使用了SSL重協商,請使用8.1及以上版本系統進行抓包。
若是確認了以上幾點,HttpCanary仍然抓包失敗,那麼極有可能使用的並不是是HTTP協議。好比像微信聊天,視頻直播等,使用的就不是HTTP協議,這種狀況須要使用其它的抓包工具,好比Packet Capture這種直接解析TCP/UDP協議的,可是每每非HTTP協議的數據包即便抓到了也沒法解析出來,由於大機率都是二進制而非文本格式的。
抓包是個技術活兒,須要對網絡協議有大體的瞭解,對抓包感興趣的同窗能夠多查閱TCP、UDP、SSL/TLS、HTTP等相關資料。
HttpCanary是專業的HTTP協議抓包工具,專一HTTP協議三十年(吹過頭了),不過目前還不支持QUIC/HTTP3這種新協議,等QUIC/HTTP3正式應用起來再說吧。