如何查看與操弄 Android/iOS App 裡的 HTTPS Request 及 Response
在 App 開發與測試的過程中,總會有幾個時刻會忍不住想著「如果可以很容易的查看與操弄 Service API 就太好了」。那麼今天就以 mitmproxy 與 MockWebServer 來示範如何在 Android 上查看與修改 HTTPS Response。
從測試的角度來說,想要查看與修改 HTTPS 回應內容時,借助 Proxy 來完成這類工作是最簡易、方便的做法了,雖然相同的工作也可以藉由開發平台內提供的開發工具來完成,但如果是 KKBOX 這種支援多種平台的服務,使用單一工具反而可以減少測試的複雜度與降低學習成本。
過去科科測試也曾經數次介紹過 Charles Proxy, Fiddler 等工具來做為查看甚至是進行網路限速等的操弄行為,但這次我選定的主角是 mitmproxy。
相較於上述兩套工具,最大的不同點是在於 mitmproxy 是一個免費且開源的自由軟體,它同時提供 Command Line、Web 與 Python API 介面,在使用上可以同時滿足手動測試與自動測試的需求!
如同多數的開源軟體,安裝起來非常容易,如果你跟我一樣使用的是 OS X 的話,透過 Homebrew 來安裝最方便不過,而且這也是官方建議的安裝方式,至於其他平台你可以在官方文件裡找到對應的安裝說明。
安裝完成後透過 --version
參數來確認安裝是否正確,如果一切無誤就可以用 Command Line 的方式來啟動 mitmproxy。
$ brew install mitmproxy$ mitmproxy --version
Mitmproxy: 4.0.4
Python: 3.7.3
OpenSSL: OpenSSL 1.1.0i 14 Aug 2018
Platform: Darwin-18.6.0-x86_64-i386-64bit$ mitmproxy
再來就是裝置上的 Proxy 設定了,這裡以 Android 模擬器為例子,hostname 的部分設為執行 mitmproxy 的 IP 位址,要特別提醒的是記得按下 APPLY
按鈕並確定 Proxy Status 顯示為 Success
來確定 Proxy 是否生效。
接著是安裝 Proxy 使用的憑證,在模擬器內使用瀏覽器開啟 “google.com” 會後會立即看到連線不安全的訊息 ,要解決這個問題請在瀏覽器內開啟 “mitm.it” 並依著畫面指示安裝對應的憑證,在正確安裝後前往 “google.com” 就不會有警告訊息了。
如果你需要進一步的安裝指引可以參考 mitmproxy 有關憑證的說明文件。
在確定 Proxy 可以正確攔截 HTTPS 訊息後,接著我們還需要一個 App 來進行實驗,我準備了一個簡單的 Android App,它會使用 Dog API 來取的隨機的 50 張狗狗圖片。
curl https://dog.ceo/api/breeds/image/random/50 | python -m json.tool{
"message": [
"https://images.dog.ceo/breeds/corgi-cardigan/n02113186_2860.jpg",
"https://images.dog.ceo/breeds/malinois/n02105162_747.jpg",
"https://images.dog.ceo/breeds/hound-afghan/n02088094_2458.jpg",
"https://images.dog.ceo/breeds/groenendael/n02105056_6778.jpg",
...
"https://images.dog.ceo/breeds/cockapoo/bubbles2.jpg"
],
"status": "success"
}
前往 Github 下載該範例 App,你可以選擇自行編譯或直接下載事先編譯好的 APK 檔案,將 App 安裝在模擬器上並開啟 App 可以看到 50 張隨機取得的狗狗圖片,App 啟動後在 mitmproxy 視窗內可以看到 App 是透過哪一個 API Endpoint 取得了什麼樣的資料。
到這裡我們終於可以開始來操弄 API 的回覆內容了,我選擇透過 Python API 的介面來進行,這個方法的好處是容易被重複使用,使用者端也不需要進行太複雜的設定動作。
首先在 mitmproxy 內查看攔截到 Request 可以得知 App 是透過https://dog.ceo/api/breeds/image/random/50
取得資料的,而 Response 是一個包含圖片 URL 的 JSON。
{
"message": [
"https://images.dog.ceo/breeds/corgi-cardigan/n02113186_2860.jpg",
"https://images.dog.ceo/breeds/malinois/n02105162_747.jpg",
"https://images.dog.ceo/breeds/hound-afghan/n02088094_2458.jpg",
"https://images.dog.ceo/breeds/groenendael/n02105056_6778.jpg",
...
"https://images.dog.ceo/breeds/cockapoo/bubbles2.jpg"
],
"status": "success"
}
有了資料結構後,變可以決定要如何置換資料,我選擇了把所有狗狗的圖片替換成這隻娃娃狗的圖片(https://i.imgur.com/qG6YmC0.jpg),mitmproxy 提供的 Python API 使用上非常直覺,透過這個腳本,當 App 端送出的 API 請求符合腳本內規範的路徑時就改送我們定義好的內容給 App。
// Full Script: https://github.com/dino-su/android-e2e-mock-example/blob/master/mitmproxy/mockDog.pyfrom mitmproxy import httpmockResponse = """{"message":[
"https:\/\/imgur.com\/qG6YmC0.jpg",
"https:\/\/imgur.com\/qG6YmC0.jpg"],
"status":"success"}"""def request(flow: http.HTTPFlow):
if flow.request.pretty_url == 'https://dog.ceo/api/breeds/image/random/50':
flow.response = http.HTTPResponse.make(200, mockResponse)
接著重新啟動 mitmproxy 並載入這個動態置換的腳本。
mitmproxy -s mitmproxy/mockDog.py
重新開啟 Android App 後就可以看到圖片已經被事先定義好的圖片取代,但因為我們只有替換 URL 所以仍然會有網路載入圖片的延遲現象,更好的做法會是用本地端的圖片來取代,除了速度更快以外,也會因為少了網路的不確定因素而讓測試更可靠。
操弄 Service 回覆內容的機制用於一些不容易準備的測試資料或是效能測試上都很有幫助,減少了跨部門溝通時常遇到的溝通與等待時間,團隊運作起來就會更有效率,測試意願也會跟著提高。
最後,如果你的測試對象只有一個 Android 平台的話,透過 MockWebServer 在 Android 的 Instrumented Test 裡建構一個 Proxy Server,這可以讓整個自動測試過程更一氣呵成,也少了不同工具間的切換流程。
有興趣的人可以參考 MockTest 與對應的 App 架構,並自行體驗一下相較於 mitmproxy 的優缺點,下一篇再來分享這裡面的一些細節 :)