你的支援對我意義重大!
? Hi,我是小彭。本文已收錄到 GitHub · Android-NoteBook 中。這裡有 Android 進階成長路線筆記 & 部落格,有志同道合的朋友,歡迎跟著我一起成長。(聯絡方式 & 入群方式在 GitHub)
前言
- 網路請求抓包是研發過程中常見問題,無論是開發時的介面除錯,還是測試時的資料檢驗,都有網路抓包的需求。隨著 HTTPS 協議的推廣以及手機系統安全性的升級,抓包的門檻可能會逐漸變高;
- 在這篇文章裡,我將帶你從原理到實戰全面認識 HTTPS 抓包,既理解 HTTPS 抓包背後的實現原理,又掌握市面上已有的抓包方案。對於一些方案中存在的坑點我也一一列舉並給出解決方法。如果能幫上忙,請務必點贊加關注,這真的對我非常重要。
1. HTTPS 工作原理
要說清楚 HTTPS 抓包的原理,首先需要先說清楚 HTTPS 實現資料安全傳輸的工作原理,主要分為三要素和三階段。如果你對 HTTPS 安全傳輸的模型還比較模糊,記得先回顧下我們之前的討論:加密、摘要、簽名、證書,一次說明白!
三要素分別是:
- 加密: 透過對稱加密演算法實現
- 認證: 透過數字簽名實現(因為私鑰只有 “合法的傳送方” 持有,其他人偽造的數字簽名無法透過驗證)
- 報文完整性: 透過數字簽名實現(因為數字簽名中使用了訊息摘要,其他人篡改的訊息無法透過驗證)
三階段分別是:
- CA 證書校驗: CA 證書校驗發生在 TLS 的前兩次握手,客戶端和服務端透過 Client Hello、Server Hello 等報文獲得服務端 CA 證書,客戶端驗證 CA 證書合法性,從而確認 CA 證書中的公鑰合法性(大多數場景不會做雙向認證,即服務端不會認證客戶端合法性,這裡先不考慮);
- 金鑰協商: 金鑰協商發生在 TLS 的後兩次握手,客戶端和服務端分別基於公鑰和私鑰進行非對稱加密通訊,協商獲得 Master Secret 對稱加密私鑰(不同演算法的協商過程細節略有不同);
- 資料傳輸: 資料傳輸發生在 TLS 握手之後,客戶端和服務端基於協商的對稱金鑰進行對稱加密通訊。
—— 圖片引用自 https 原理模板
2. HTTPS 抓包原理 - 中間人攻擊
我們熟悉的 Fiddler、Charles 和 HttpCanary App 等抓包工具,其實都是採用了中間人攻擊(Man-in-the-MiddleAttack,MITM)的方案: 將客戶端的網路流量代理到 MITM 主機,再透過一系列的皮膚或工具將網路請求結構化地呈現出來。
如果攔截的是 HTTP 請求還好說,要是攔截的是 HTTPS 請求,首先就遇到第一個問題 —— 加密:
- 加密: 由於 HTTPS 通訊中對稱金鑰 Master Secret 只有通訊雙方才持有,MITM 無法解密密文,導致在抓包工具上也只能看到一堆無意義的亂碼。
要解決這個問題,只能想辦法讓 MITM 也獲得這個對稱金鑰。此時,MITM 不僅要做流量攔截,還需要偽裝成真實的客戶端和服務端,與真實的通訊雙方分別建立獨立的連線。我們來看下在中間人攻擊下,HTTPS 的三階段:
連線 1:客戶端與中間人的 HTTPS 連線:
- CA 證書校驗: 客戶端與 MITM 握手,MITM 返回一個 “調包” 的 CA 證書(為了讓客戶端驗證 CA 證書透過,需要提前在系統上安裝 MITM 的證書);
- 金鑰協商: 客戶端和 MITM 基於 “調包” 的公鑰和私鑰進行非對稱加密通訊,協商獲得對稱金鑰;
- 資料傳輸: 客戶端和 MITM 基於協商的對稱金鑰進行對稱加密通訊,此時 MITM 就可以解密出明文。
連線 2:中間人與服務端的 HTTPS 連線:
- CA 證書校驗: MITM 與 服務端握手,服務端返回 CA 證書,由於服務端證書本來就是合法的,因此 MITM 可以拿到服務端公鑰;
- 金鑰協商: MITM 和服務端分別基於公鑰和私鑰進行非對稱加密通訊,協商獲得 Master Secret 對稱加密私鑰;
- 資料傳輸: MITM 和服務端基於協商的對稱金鑰進行對稱加密通訊。
到這裡,MITM 就成功與真實的客戶端和服務端建立了獨立的連線,傳送的密文在 MITM 上就可以成功解密出來了。
既然 HTTPS 可以被抓包,是否說明 HTTPS 不安全的?
這需要區分不同使用場景下安全的口徑,在使用者不知情的場景 HTTPS 完全可以保證資料安全傳輸,而對於使用者主動授權的場景,使用者需要為這個主動的行為承擔相應的安全風險。
總結一下實現 HTTPS 抓包的基本步驟:
- 部署 MITM 代理伺服器;
- 透過代理等方式將網路流量歸集到 MITM 主機;
- 客戶端信任 MITM CA 證書;
- MITM 分別與客戶端和服務端建立獨立連線;
- MITM 解密客戶端和服務端的通訊,並結構化地呈現出來。
3. 在 Android 上安裝 CA 證書
在 Android 上安裝 CA 證書,可以總結為三種,其中系統證書和使用者證書都可以在系統設定中 信任的憑據
中檢視:
- 系統證書: 系統 CA 證書安裝在
/system/etc/security/cacerts/
目錄,只允許 Root 許可權才能進行新增和刪除。需要注意系統 CA 證書有特殊的命名格式:雜湊值.0
,轉換方法可以參考:Android 內建證書檔案。 - 使用者證書: 使用者 CA 證書安裝在
/data/misc/user/0/cacerts-added/
目錄,由使用者自行安裝,可以在系統設定安裝證書
中進行安裝。由於 Android 7.0 行為變更,對於 targetSdkVersion ≥ 24 的應用,系統不再預設信任使用者證書,需要在配置中額外宣告:
應用級 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<!-- Trust preinstalled CAs -->
<certificates src="system" />
<!-- HERE: Additionaly trus user added CAs -->
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
- 應用固定證書(Certificate Pinner): 客戶端可以直接內建信任的服務端證書來限制其接受的證書集,用自定義的 TrustManager 代替系統預設的 TrustManager。在 HTTPS 請求的證書校驗階段,只有其接受的證書集合可以校驗透過,這也是規避中間人攻擊的一種方案。例如:
protected static SSLSocketFactory getSSLSocketFactory(Context context, @ResId int[] certificates) {
if (context == null) {
throw new NullPointerException("context == null");
}
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
// Create a KeyStore containing our trusted CAs
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
for (int i = 0; i < certificates.length; i++) {
// 載入內建證書
InputStream is = context.getResources().openRawResource(certificates[i]);
keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(is));
if (is != null) {
is.close();
}
}
// Create a TrustManager that trusts the CAs in our keyStore
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 透過 OkHttp 的 API 設定證書
OkHttpClient.Builder builder = new OkHttpClient.Builder();
int[] certficates = new int[]{R.raw.media};
builder.socketFactory(getSSLSocketFactory(context, certficates));
...
突破 Android 7.0 使用者 CA 證書限制
由於 Android 7.0 行為變更,對於 targetSdkVersion ≥ 24 的應用,系統不再預設信任使用者證書,需要在應用的 AndroidManifest.xml 中增加 android:networkSecurityConfig 配置。如果你要抓包第三方應用,並且該應用沒有配置,就需要採用一些手段來突破限制了:
- 方法 1 - 使用 Android 7.0 以下系統: 從源頭抹平使用者證書限制,這個最簡單直接;
- 方法 2 - 使用平行空間等虛擬系統: 使用 HttpCanary 平行空間或 VMOS App 等虛擬系統,在手機上虛擬出 Android 7.0 以下系統環境;
- 方法 3 - 安裝證書到系統證書目錄: 需要 Root 許可權,把 CA 證書直接安裝到系統證書目錄下。
4. Fiddler 使用技巧總結
Fiddler 目前主要是用在 Window 系統上的網路除錯工具,最新版本的 Fiddler Everywhere 開始支援全平臺了,但我體驗下來覺得不少功能是缺失的,期待官方更新。下面的介紹是採用新版 Fiddler Everywhere,操作與舊版本上是類似的。
4.1 使用 Fiddler 進行 HTTPS 抓包
這裡總結一下使用 Fiddler 進行抓包的主要步驟,其實就是按照 第 2 節 提到的 實現 HTTPS 抓包的基本步驟 的思路進行配置:
- 1、部署 MITM 代理伺服器: 在電腦上啟動 Fiddler,預設會在這臺電腦的 8866 埠部署 Fiddler 的 Web 伺服器,可以透過下圖設定頁面修改埠。因為我們是在手機上抓包,所以還必須勾選
Allow remote computers to connect
:
- 2、透過代理等方式將網路流量歸集到 MITM 主機: 在電腦命令列執行 ipconfig 獲得本地 IP 地址,然後將手機和電腦連線到同一個區域網下,再修改手機連線 Wifi 的高階設定,設定代理到電腦的 IP 地址和 8866 埠號。至此,你就可以在 Fiddler 上看到抓取的網路請求了,但還看不到 HTTPS 協議傳輸的資料,介面上會有一個 “加鎖” 的圖示提示:
- 3、客戶端信任 MITM CA 證書: 首先需要在 Fiddler 上開啟 Https 抓包功能,才能匯出證書,在下圖設定頁面中,先點選
Trust root certificate
,再勾選Capture HTTPS traffic
選項。
然後,你需要把 CA 證書安裝到手機上,有兩種方式:先使用 Export root certificate
匯出證書檔案(預設匯出到桌面),再自行將檔案傳送到手機上;也可以在手機瀏覽器上訪問 ipv4.fiddler:8866/
,點選 FiddlerRoot certificate
直接下載證書。
現在你已經成功把 CA 證書下載到手機上了,還需要手動安裝證書。在系統設定中搜尋 安裝證書
,找到剛才下載的 CA 證書並安裝(不同手機系統介面不同):
到這裡,你已經順利地在 Fiddler 上抓取到 HTTPS 請求了。最好在篩選欄上過濾干擾項,比如不重要的域名,CONNECT 握手驗證請求:
- 過濾域名:contains juejin
- 過濾 Method:is not equals to CONNECT、HEAD
- 過濾 Content-Type:does not contains image/
提示: 事實上,Fiddler 能做的不止是 HTTP 抓包這麼簡單,它還支援非常多高階功能。如果你需要深入研究 Fiddler,建議你直接購買肖佳寫的《HTTP抓包實戰》,下面我總結一些我玩過的一些技巧。
4.2 Fiddler 報文重放測試
重放攻擊(Replay Attacks)是指攻擊者透過抓包的方式,得到一個客戶端向服務端傳送的真實請求報文,並重復傳送給服務端的攻擊行為。比如攻擊者抓取到一個點贊、投票或領獎的請求報文,並重復傳送給伺服器。因為這個請求本身就是合法的,而且攻擊者並未篡改請求內容,所以服務端會誤以為是一個真實的有效請求。
在 Fiddler 上重放請求很簡單,有兩種方式:右鍵點選一個請求,選擇 Replay→Reissue Requests
直接重放;或者選擇 Edit in Composer
先編輯再重放。
我在專案中實際遇到過重放攻擊,一個比較聰明的使用者抓取到了 App 類似領金幣的請求報文,然後用重放攻擊薅了一波羊毛。防範重放方法是在請求中增加標識引數和數字簽名(防篡改):
- 時間戳: 服務端將當前請求的時間戳與伺服器時間對比,如果超過了閾值(如 60s),則判定為過時請求。缺點是 60s 內的重放請求依然會被判定為有效;
- 流水號: 服務端將當前請求的流水號與服務端記錄的流水號對比,如果收到一個非升序(相等或小於)則判定為過時請求。缺點是需要保證報文順序。
- 一次性口令: 服務端用當前請求的一次性口令在服務端維護的口令表中查詢,如果已經使用過該口令則判斷為過時請求。缺點是需要維護口令表,實踐中可以綜合使用時間戳 + 一次性口令的方案,這樣既避免了短時間內的重放攻擊,服務端也只需要維護一小段時間視窗內的口令表。
4.3 Fiddler 弱網模擬
目前最新版本的 Fiddler Everywhere 還不支援弱網模擬,需要使用舊版本的 Fiddler Classic,配置路徑為:Rules→Performances→Simulate Modem Speeds
。
4.4 Fiddler 修改 HTTP 請求
Fiddler 本身是一個代理伺服器,可以攔截 HTTP 請求 / 響應進行修改後再放行。在 Fiddler 上配置 Rule:
5. Charles 使用技巧總結
5.1 使用 Charles 進行 HTTPS 抓包
這裡總結一下使用 Charles 進行抓包的主要步驟,其實就是按照 第 2 節 提到的 實現 HTTPS 抓包的基本步驟 的思路進行配置:
- 1、部署 MITM 代理伺服器: 在電腦上啟動 Charles,預設會在這臺電腦的 8888 埠部署 Charles 的 Web 伺服器,可以透過下圖設定頁面修改埠,這裡最好不要使用預設埠號。
- 2、透過代理等方式將網路流量歸集到 MITM 主機: 在電腦命令列執行 ipconfig 獲得本地 IP 地址(也可以透過 Charles 的
Help→Local Ip Address
檢視),然後將手機和電腦連線到同一個區域網下,再修改手機連線 Wifi 的高階設定,設定代理到電腦的 IP 地址和 8888 埠號。 - 3、客戶端信任 MITM CA 證書: Charles 抓包也需要在手機上信任 Charles CA 證書。根據指引,會讓你在手機瀏覽器訪問
chls.pro/ssl
下載證書,安裝證書的方法與 Fiddler 相同(踩坑記錄:使用預設埠號 8888 時,訪問 chls.pro/ssl 一直不會自動下載,修改埠號就可以了)。
到這裡,你已經順利地在 Charles 上抓取到 HTTPS 請求了。最好在篩選欄中過濾掉干擾項:
5.2 Charles 報文重放測試
在 Charles 上重放請求很簡單,有兩種方式:右鍵點選一個請求,選擇 Repeat
或 Repeat Advanced
直接重放;或者選擇 Compose
先編輯再重放。其中 Repeat Advanced 適合做壓力測試。
5.3 Charles 弱網模擬
開啟 Proxy→Throttle Settings
進入弱網配置頁面,其中 Throttle preset
提供了多種預置的網路環境模擬配置,可以直接在此基礎上修改。各個選項的含義如下:
- Bandwidth 頻寬: 單位時間內透過通訊鏈路的資料量,即每秒傳輸的資料量;
- Utilisation 利用率: 頻寬利用率;
- Round-trip latency 往返時延: 傳輸層概念,指報文在客戶端和服務端之間一次往返通訊的時延;
- MTU 最大傳輸單元: 資料鏈路層概念,指資料幀可攜帶最大的有效載荷,在乙太網中一般是 1500 位元組。如果一個 IP 包大小超過了 MTU,則會進行 IP 分片;
- Reliability 可靠性 / 丟包率: 指資料傳輸失敗的機率;
- Stability 穩定性 / 抖動率: 指網路環境的穩定性,如果網路不穩定,則資料傳輸的成功率也會不穩定。這個引數非常適合模擬行動網路;
- Unstable quality range 不穩定質量範圍: 指網路環境的不穩定範圍。
5.4 Charles 修改 HTTP 請求
Charles 本身是一個代理伺服器,可以攔截 HTTP 請求 / 響應進行修改後再放行。在 Charles 上配置 Rewrite:
6. 手機本地抓包方案
前面提到的 Fiddler、Charles 或者 Wireshark 等抓包方案的整個配置過程是比較繁瑣的,比如需要配置手機代理、安裝證書等。最大的缺點是都依賴於一臺部署代理伺服器的電腦,不能滿足隨時隨地抓包的需求。實踐中可以採用綜合的抓包方案:在手機上使用本地抓包方案,無法滿足需求時再使用 Fiddler 等方案補齊。
6.1 VPNService API
VPNService 是 Android 4.0 引入的 API,能夠對系統流量進行擷取且不需要 root 許可權。我們熟悉的科學上網 Ap,其實就是執行了一個 VPNService 服務。在 VPN 執行時,通常在通知欄上會有相關的提示,比如 “已啟用 VPN” 等。在系統設定中搜尋 VPN
,可以檢視當前手機中提供 VPN 服務的應用,例如:
- HttpCanary App
HttpCanary 是一款強大的針對安卓手機的網路分析工具,它的工作原理是基於 VPNService 部署了一個 MITM 代理伺服器來抓取網路資料包。另外,HttpCanary 還支援破解雙向認證抓包,具體操作參考:Android 平臺 HTTPS 抓包全方案
不過,HttpCanary 在 Android 11 系統上抓取 HTTPS 請求會有問題,由於系統只允許從系統設定中手動選擇安裝證書,而目前 App 還沒有適配這個問題,導致安裝證書這一步就涼了。我提供兩個解決方法:
- 1、使用低版本的手機(極其舒適);
- 2、使用有 Root 許可權的手機,手動把從 HttpCanary 內部儲存空間中把證書撈出來,再轉化為系統證書並安裝。這個方案順便還解決了第三方應用未配置 android:networkSecurityConfig 的問題。具體操作參考:安卓 11 httpcanary 小黃鳥系統證書的安裝
- 有贊移動助手 App
有贊技術團隊是我經常關注的團隊之一,有贊移動助手 App 本地抓包方案 是他們 19 年分享的一個手機本地抓包方案。有贊助手 App 的原理也是基於 Android VPNService 或 IOS NetworkExtension 搭建的代理伺服器,由助手 App 與真實伺服器完成 HTTP 請求,相當於是自實現的 HttpCanary。目前以小彭的積累還不能完全消化實現這個方案,就記錄在這裡提供個思路吧。
6.2 OkHttp 攔截器
對於基於 OkHttp 實現網路請求的應用,可以透過攔截器監控應用內的網路資料,再透過通知欄、桌面小部件等入口檢視抓取的資料。目前業內已經有開源的實現可直接使用:
- Chunk: 整合 Chunk 後,通知欄訊息會顯示監控到的網路請求,點選後可以進入分析頁面。不過這個專案已經多年沒有維護了,而且也不支援資料篩選,有些雞肋。
- 滴滴 DoraemonKit
滴滴的 哆啦A夢 大家都比較熟悉了,是一款面向大前端產品的效率平臺,目前已經發展成一個相對完整的生態,支援 Android 和 iOS 等多個平臺。DoraemonKit 也提供了網路監聽的能力,其原理同樣是基於 OkHttp 攔截器。區別在於 DoraemonKit 是透過 AOP 注入的方式新增攔截器,所以初始化時不需要我們手動新增攔截器,這也很好地解決不同元件存在多個 OkHttpClient 的問題。相關原始碼:
HttpUrlConnectionProxyUtil
private static void addInterceptor(OkHttpClient.Builder builder) {
// 判斷當前是否已經新增了攔截器,如果已新增則返回
for (Interceptor interceptor : builder.interceptors()) {
if (interceptor instanceof DokitMockInterceptor) {
return;
}
}
builder
//新增mock攔截器
.addInterceptor(new DokitMockInterceptor())
//新增大圖檢測攔截器
.addInterceptor(new DokitLargePicInterceptor())
//新增dokit攔截器
.addInterceptor(new DokitCapInterceptor())
//新增弱網 攔截器
.addNetworkInterceptor(new DokitWeakNetworkInterceptor())
// 新增擴充套件攔截器
.addInterceptor(new DokitExtInterceptor());
}
- 優酷 Ribut
Ribut 是小彭交流群中的同學分享的,看了下目前這個專案還處於比較初期的階段,期待後續的發展。它是阿里巴巴優酷技術團隊研發的視覺化除錯架構,目標是透過工具化手段解決研發日常痛點問題,如網路抓包。其中網路抓包的大概思路分為兩步:1、透過掃碼建立與 PC 端連線;2、透過 OkHttp 攔截器抓取網路請求資料並轉發到 PC 端。雖然嚴格來說還是依賴 PC 端,但是整體的體驗是 OK 的。
7. 總結
說了這麼多抓包的方案,讓我們換個視角,App 反抓包有哪些方案,你知道嗎?關注我,帶你瞭解更多,我們下次見。
參考資料
- 《HTTP 抓包實戰》—— 肖佳 著
- 網路安全配置 —— Android 官方文件
- Capturing and Inspecting Android Traffic —— Fiddler 官方文件
- Install System CA Certificate on Android Emulator —— mitmproxy 文件
- Android 平臺 HTTPS 抓包全方案 —— MegatronKing 著
- Https 在 Android 中的使用新增可信證書認證 —— JoeLitterStar 著
- 有贊移動助手 App 本地抓包方案 —— 楊彬(有贊)著
你的點贊對我意義重大!微信搜尋公眾號 [彭旭銳],希望大家可以一起討論技術,找到志同道合的朋友,我們下次見!