前言
- 現在很多App裡都內建了Web網頁(Hyprid App),比如說很多電商平臺,淘寶、京東、聚划算等等,如下圖
- 上述功能是由 Android的WebView 實現的,但是 WebView 使用過程中存在許多漏洞,容易造成使用者資料洩露等等危險,而很多人往往會忽視這個問題
- 今天我將全面介紹 Android WebView的使用漏洞 及其修復方式
閱讀本文前請先閱讀:
Android開發:最全面、最易懂的Webview詳解
最全面總結 Android WebView與 JS 的互動方式
手把手教你構建 Android WebView 的快取機制 & 資源預載入方案
目錄
1. 型別
WebView中,主要漏洞有三類:
- 任意程式碼執行漏洞
- 密碼明文儲存漏洞
- 域控制不嚴格漏洞
2. 具體分析
2.1 WebView 任意程式碼執行漏洞
出現該漏洞的原因有三個:
- WebView 中
addJavascriptInterface()
介面 - WebView 內建匯出的
searchBoxJavaBridge_
物件 - WebView 內建匯出的
accessibility
和accessibilityTraversal
Object 物件
2.1.1 addJavascriptInterface 介面引起遠端程式碼執行漏洞
A. 漏洞產生原因
JS呼叫Android的其中一個方式是通過addJavascriptInterface
介面進行物件對映:
webView.addJavascriptInterface(new JSObject(), "myObj");
// 引數1:Android的本地物件
// 引數2:JS的物件
// 通過物件對映將Android中的本地物件和JS中的物件進行關聯,從而實現JS呼叫Android的物件和方法複製程式碼
所以,漏洞產生原因是:當JS拿到Android這個物件後,就可以呼叫這個Android物件中所有的方法,包括系統類(java.lang.Runtime 類),從而進行任意程式碼執行。
如可以執行命令獲取本地裝置的SD卡中的檔案等資訊從而造成資訊洩露
具體獲取系統類的描述:(結合 Java 反射機制)
- Android中的物件有一公共的方法:getClass() ;
- 該方法可以獲取到當前類 型別Class
- 該類有一關鍵的方法: Class.forName;
- 該方法可以載入一個類(可載入 java.lang.Runtime 類)
- 而該類是可以執行本地命令的
以下是攻擊的Js核心程式碼:
function execute(cmdArgs)
{
// 步驟1:遍歷 window 物件
// 目的是為了找到包含 getClass ()的物件
// 因為Android對映的JS物件也在window中,所以肯定會遍歷到
for (var obj in window) {
if ("getClass" in window[obj]) {
// 步驟2:利用反射呼叫forName()得到Runtime類物件
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
// 步驟3:以後,就可以呼叫靜態方法來執行一些命令,比如訪問檔案的命令
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
// 從執行命令後返回的輸入流中得到字串,有很嚴重暴露隱私的危險。
// 如執行完訪問檔案的命令之後,就可以得到檔名的資訊了。
}
}
}複製程式碼
- 當一些 APP 通過掃描二維碼開啟一個外部網頁時,攻擊者就可以執行這段 js 程式碼進行漏洞攻擊。
- 在微信盛行、掃一掃行為普及的情況下,該漏洞的危險性非常大
B. 解決方案
B1. Android 4.2版本之後
Google 在Android 4.2 版本中規定對被呼叫的函式以 @JavascriptInterface
進行註解從而避免漏洞攻擊
B2. Android 4.2版本之前
在Android 4.2版本之前採用攔截prompt()進行漏洞修復。
具體步驟如下:
-
繼承 WebView ,重寫
addJavascriptInterface
方法,然後在內部自己維護一個物件對映關係的 Map;將需要新增的 JS 介面放入該Map中
-
每次當 WebView 載入頁面前載入一段本地的 JS 程式碼,原理是:
- 讓JS呼叫一Javascript方法:該方法是通過呼叫prompt()把JS中的資訊(含特定標識,方法名稱等)傳遞到Android端;
- 在Android的onJsPrompt()中 ,解析傳遞過來的資訊,再通過反射機制呼叫Java物件的方法,這樣實現安全的JS呼叫Android程式碼。
關於Android返回給JS的值:可通過prompt()把Java中方法的處理結果返回到Js中
具體需要載入的JS程式碼如下:
javascript:(function JsAddJavascriptInterface_(){
// window.jsInterface 表示在window上宣告瞭一個Js物件
// jsInterface = 註冊的物件名
// 它註冊了兩個方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2)
// 如果有返回值,就新增上return
if (typeof(window.jsInterface)!=`undefined`) {
console.log(`window.jsInterface_js_interface_name is exist!!`);}
else {
window.jsInterface = {
// 宣告方法形式:方法名: function(引數)
onButtonClick:function(arg0) {
// prompt()返回約定的字串
// 該字串可自己定義
// 包含特定的識別符號MyApp和 JSON 字串(方法名,引數,物件名等)
return prompt(`MyApp:`+JSON.stringify({obj:`jsInterface`,func:`onButtonClick`,args:[arg0]}));
},
onImageClick:function(arg0,arg1,arg2) {
return
prompt(`MyApp:`+JSON.stringify({obj:`jsInterface`,func:`onImageClick`,args:[arg0,arg1,arg2]}));
},
};
}
}
)()
// 當JS呼叫 onButtonClick() 或 onImageClick() 時,就會回撥到Android中的 onJsPrompt ()
// 我們解析出方法名,引數,物件名
// 再通過反射機制呼叫Java物件的方法複製程式碼
關於該方法的其他細節
細節1:載入上述JS程式碼的時機
- 由於當 WebView 跳轉到下一個頁面時,之前載入的 JS 可能已經失效
- 所以,通常需要在以下方法中載入 JS:
onLoadResource();
doUpdateVisitedHistory();
onPageStarted();
onPageFinished();
onReceivedTitle();
onProgressChanged();複製程式碼
細節2:需要過濾掉 Object 類的方法
- 由於最終是通過反射得到Android指定物件的方法,所以同時也會得到基類的其他方法(最頂層的基類是 Object類)
- 為了不把 getClass()等方法注入到 JS 中,我們需要把 Object 的共有方法過濾掉,需要過濾的方法列表如下:
getClass()
hashCode()
notify()
notifyAl()
equals()
toString()
wait()複製程式碼
總結
- 對於Android 4.2以前,需要採用攔截prompt()的方式進行漏洞修復
- 對於Android 4.2以後,則只需要對被呼叫的函式以 @JavascriptInterface進行註解
- 關於 Android 系統佔比,Google公佈的資料:截止 2017 .1 .8 ,Android4.4 之下佔有約15%,所以需要重視。
具體資料如下:
2.1.2 searchBoxJavaBridge_介面引起遠端程式碼執行漏洞
A. 漏洞產生原因
- 在Android 3.0以下,Android系統會預設通過
searchBoxJavaBridge_
的Js介面給 WebView 新增一個JS對映物件:searchBoxJavaBridge_
物件 - 該介面可能被利用,實現遠端任意程式碼。
B. 解決方案
刪除searchBoxJavaBridge_
介面
// 通過呼叫該方法刪除介面
removeJavascriptInterface();複製程式碼
2.1.3 accessibility
和 accessibilityTraversal
介面引起遠端程式碼執行漏洞
問題分析與解決方案同上,這裡不作過多闡述。
2.2 密碼明文儲存漏洞
2.2.1 問題分析
WebView預設開啟密碼儲存功能 :
mWebView.setSavePassword(true)`複製程式碼
- 開啟後,在使用者輸入密碼時,會彈出提示框:詢問使用者是否儲存密碼;
- 如果選擇”是”,密碼會被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險
2.2.2 解決方案
關閉密碼儲存提醒
WebSettings.setSavePassword(false)複製程式碼
2.3 域控制不嚴格漏洞
2.3.1 問題分析
先看Android裡的 WebViewActivity.java:
public class WebViewActivity extends Activity {
private WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webView = (WebView) findViewById(R.id.webView);
//webView.getSettings().setAllowFileAccess(false); (1)
//webView.getSettings().setAllowFileAccessFromFileURLs(true); (2)
//webView.getSettings().setAllowUniversalAccessFromFileURLs(true); (3)
Intent i = getIntent();
String url = i.getData().toString(); //url = file:///data/local/tmp/attack.html
webView.loadUrl(url);
}
}
/**Mainifest.xml**/
// 將該 WebViewActivity 在Mainifest.xml設定exported屬性
// 表示:當前Activity是否可以被另一個Application的元件啟動
android:exported="true"複製程式碼
即 A 應用可以通過 B 應用匯出的 Activity 讓 B 應用載入一個惡意的 file 協議的 url,從而可以獲取 B 應用的內部私有檔案,從而帶來資料洩露威脅
具體:當其他應用啟動此 Activity 時, intent 中的 data 直接被當作 url 來載入(假定傳進來的 url 為 file:///data/local/tmp/attack.html ),其他 APP 通過使用顯式 ComponentName 或者其他類似方式就可以很輕鬆的啟動該 WebViewActivity 並載入惡意url。
下面我們著重分析WebView中getSettings類的方法對 WebView 安全性的影響:
- setAllowFileAccess()
- setAllowFileAccessFromFileURLs()
- setAllowUniversalAccessFromFileURLs()
1. setAllowFileAccess()
// 設定是否允許 WebView 使用 File 協議
webView.getSettings().setAllowFileAccess(true);
// 預設設定為true,即允許在 File 域下執行任意 JavaScript 程式碼複製程式碼
使用 file 域載入的 js程式碼能夠使用進行同源策略跨域訪問,從而導致隱私資訊洩露
- 同源策略跨域訪問:對私有目錄檔案進行訪問
- 針對 IM 類產品,洩露的是聊天資訊、聯絡人等等
- 針對瀏覽器類軟體,洩露的是cookie 資訊洩露。
如果不允許使用 file 協議,則不會存在上述的威脅;
webView.getSettings().setAllowFileAccess(true);複製程式碼
但同時也限制了 WebView 的功能,使其不能載入本地的 html 檔案,如下圖:
移動版的 Chrome 預設禁止載入 file 協議的檔案
解決方案:
- 對於不需要使用 file 協議的應用,禁用 file 協議;
setAllowFileAccess(false);複製程式碼
- 對於需要使用 file 協議的應用,禁止 file 協議載入 JavaScript。
setAllowFileAccess(true);
// 禁止 file 協議載入 JavaScript
if (url.startsWith("file://") {
setJavaScriptEnabled(false);
} else {
setJavaScriptEnabled(true);
}複製程式碼
2. setAllowFileAccessFromFileURLs()
// 設定是否允許通過 file url 載入的 Js程式碼讀取其他的本地檔案
webView.getSettings().setAllowFileAccessFromFileURLs(true);
// 在Android 4.1前預設允許
// 在Android 4.1後預設禁止複製程式碼
當AllowFileAccessFromFileURLs()
設定為 true 時,攻擊者的JS程式碼為:
// 通過該程式碼可成功讀取 /etc/hosts 的內容資料複製程式碼
解決方案:設定setAllowFileAccessFromFileURLs(false);
當設定成為 false 時,上述JS的攻擊程式碼執行會導致錯誤,表示瀏覽器禁止從 file url 中的 javascript 讀取其它本地檔案。
3. setAllowUniversalAccessFromFileURLs()
// 設定是否允許通過 file url 載入的 Javascript 可以訪問其他的源(包括http、https等源)
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
// 在Android 4.1前預設允許(setAllowFileAccessFromFileURLs()不起作用)
// 在Android 4.1後預設禁止複製程式碼
當AllowFileAccessFromFileURLs()
被設定成true時,攻擊者的JS程式碼是:
// 通過該程式碼可成功讀取 http://www.so.com 的內容
複製程式碼
解決方案:設定setAllowUniversalAccessFromFileURLs(false);
4. setJavaScriptEnabled()
// 設定是否允許 WebView 使用 JavaScript(預設是不允許)
webView.getSettings().setJavaScriptEnabled(true);
// 但很多應用(包括移動瀏覽器)為了讓 WebView 執行 http 協議中的 JavaScript,都會主動設定為true,不區別對待是非常危險的。複製程式碼
即使把setAllowFileAccessFromFileURLs()
和setAllowUniversalAccessFromFileURLs()
都設定為 false,通過 file URL 載入的 javascript 仍然有方法訪問其他的本地檔案:符號連結跨源攻擊
前提是允許 file URL 執行 javascript,即
webView.getSettings().setJavaScriptEnabled(true);
這一攻擊能奏效的原因是:通過 javascript 的延時執行和將當前檔案替換成指向其它檔案的軟連結就可以讀取到被符號連結所指的檔案。具體攻擊步驟:
- 把惡意的 js 程式碼輸出到攻擊應用的目錄下,隨機命名為 xx.html,修改該目錄的許可權;
- 修改後休眠 1s,讓檔案操作完成;
- 完成後通過系統的 Chrome 應用去開啟該 xx.html 檔案
- 等待 4s 讓 Chrome 載入完成該 html,最後將該 html 刪除,並且使用 ln -s 命令為 Chrome 的 Cookie 檔案建立軟連線
注:在該命令執行前 xx.html 是不存在的;執行完這條命令之後,就生成了這個檔案,並且將 Cookie 檔案連結到了 xx.html 上。
於是就可通過連結來訪問 Chrome 的 Cookie
- Google 沒有進行修復,只是讓Chrome 最新版本預設禁用 file 協議,所以這一漏洞在最新版的 Chrome 中並不存在
- 但是,在日常大量使用 WebView 的App和瀏覽器,都有可能受到此漏洞的影響。通過利用此漏洞,容易出現資料洩露的危險
如果是 file 協議,禁用 javascript 可以很大程度上減小跨源漏洞對 WebView 的威脅。
- 但並不能完全杜絕跨原始檔洩露。
- 例:應用實現了下載功能,對於無法載入的頁面,會自動下載到 sd 卡中;由於 sd 卡中的檔案所有應用都可以訪問,於是可以通過構造一個 file URL 指向被攻擊應用的私有檔案,然後用此 URL 啟動被攻擊應用的 WebActivity,這樣由於該 WebActivity 無法載入該檔案,就會將該檔案下載到 sd 卡下面,然後就可以從 sd 卡上讀取這個檔案了
最終解決方案
-
對於不需要使用 file 協議的應用,禁用 file 協議;
// 禁用 file 協議; setAllowFileAccess(false); setAllowFileAccessFromFileURLs(false); setAllowUniversalAccessFromFileURLs(false);複製程式碼
-
對於需要使用 file 協議的應用,禁止 file 協議載入 JavaScript。
// 需要使用 file 協議
setAllowFileAccess(true);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
// 禁止 file 協議載入 JavaScript
if (url.startsWith("file://") {
setJavaScriptEnabled(false);
} else {
setJavaScriptEnabled(true);
}複製程式碼
3. 總結
- 本文主要對Android WebView的使用漏洞及其修復方式進行了全面介紹
- 關於WebView的系列文章希望對你有所幫助
Android開發:最全面、最易懂的Webview詳解
最全面總結 Android WebView與 JS 的互動方式
手把手教你構建 Android WebView 的快取機制 & 資源預載入方案 - 接下來我會繼續講解其他安卓開發的知識,有興趣可以繼續關注Carson_Ho的安卓開發筆記!!!!