Charles 在 Android 7.0 上會 Client SSL handshake failed ?
叮,成功觸發隱藏 BUG
最近打 Release 包提測後,用 Charles 代理專案,偶然發現在某些裝置上會代理失敗。而且很無語的是,當時的場景是周圍的小夥伴們都沒有出現這個問題,只有我總是代理失敗。這莫名的熟悉感,我知道我可能又要觸發一個隱藏 BUG 了。
看下代理失敗的具體表現:
Client SSL handshake failed: An unknown issue occurred processing the certificate (certificate_unknown)
- Android 6.0 debug 和 release 版本表現正常
- Android 7.0 debug 版本代理正常,release 版本失敗
- Android 8.0 debug 版本代理正常,release 版本失敗
然後經過網路搜尋和專案的特色配置等各種排查,我發現了一絲線索,首先在 AndroidManifest.xml 中的 application 標籤多了平常沒有看到過的一個屬性 networkSecurityConfig:
<application
android:networkSecurityConfig="@xml/network_security_config"
//...其他屬性省略
>
//....
</application>
res/xml/network_security_config.xml 的內容如下:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <debug-overrides cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system" /> <certificates src="user" /> </trust-anchors> </debug-overrides> </network-security-config>
其中 debug-overrides 在我眼中幾乎是大寫加粗般的效果,猜測 release 不能代理十有八九是因為這個原因。
然後我就把它給註釋掉,換成 <base-config>
, 重新打 release 包試了一下,果然它可以正常代理了。
解決問題之後,心中還存在幾個疑問:
- networkSecurityConfig 是什麼?
- 為什麼Android 6.0 正常, Android 7.0 就不行了呢?
很開心的是,這些疑問在官方文件-網路安全性配置上都得到了很好的解釋,下面就是我給自己的答案。
networkSecurityConfig 的意義
官方文件對它的解釋是:
網路安全性配置特性讓應用可以在一個安全的宣告性配置檔案中自定義其網路安全設定,而無需修改應用程式碼
networkSecurityConfig 是網路安全性配置特性,而res/xml/network_security_config.xml 就是相關的自定義配置檔案。
在自定義配置檔案中可以做這四種事:
-
自定義信任錨:針對應用的安全連線自定義哪些證書頒發機構 (CA) 值得信任。
對應的標籤是
<trust-anchors>
,上文中也有出現,中間包含信任的證書。支援的證書可以分三類,system,user,raw/xxx,分別是裝置系統證書,裝置使用者新增證書,應用 raw 資料夾配置的證書檔案。 -
僅除錯重寫:在應用中以安全方式除錯安全連線,而不會增加已安裝使用者的風險。
這個就是上文中的罪魁禍首
<debug-overrides>
,用上了這個,就能保證只在debuggable = true
的除錯模式下,信任標籤內的證書了。 -
明文通訊選擇退出:防止應用意外使用明文通訊。
就是上文出現的
cleartextTrafficPermitted="true"
,含義是是否允許明文傳輸,例如 https 通訊中突然出現了 http 明文通訊,可以用這個屬性決定是允許繼續通訊或者直接
退出。 -
證書固定:將應用的安全連線限制為特定的證書。
使用的標籤是
<pin-set>
,不知道什麼情況下會用到這個,有興趣的可以自己看官方文件。
為什麼 Android 7.0 開始就需要特殊處理?
出於網路安全的角度考慮,預設情況下,面向 Android 7.0 的應用開始只信任系統提供的證書(system),且不再信任使用者新增的證書(user)。
預設配置如下:
≥ Android 7.0:
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
< Android 7.0:
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
Charles 代理 https 的時候,需要在手機上安裝對應的 charles-ssl 證書,這個證書屬於使用者級別的證書。所以,同樣的情況下,Android 6.0 可以被代理成功,而Android 7.0 及以上都顯示 lient SSL handshake failed。所以如果想代理 Android 7.0及以上版本,需要手動設定 application的 networkSecurityConfig 屬性。
很明顯,我遇到的情況中,這個問題已經被其他小夥伴發現,但是不知道出於什麼考慮 <debug-overrides>
限制,導致了上述發生的問題。經過和相關他同事溝通,沒有加這個限制的必要,已經去掉了。不過對於有相關安全需求的小夥伴們, <debug-overrides>
是一個很好的安全方案。
寫在後面
最近換了新的工作,因為要重新熟悉專案程式碼和工作流程和節奏,部落格和公眾號很久都沒更新了,很感謝小夥伴們還沒有取關我,並且還收到幾條讓人非常感動的私信,其中還有個非常有愛的妹紙,但是看到的時間比較晚,不在24小時內就沒法回覆了,非常遺憾。
總之謝謝朋友們,接下來部落格和公眾號會正常更新的,歡迎點贊,留言,私信交流哦!
最後,萬聖節快樂!
歡迎關注個人微信公眾號,最新的部落格,好玩的事情,都會在上面分享,期待與你共同成長。