Android WebView開發使用中必須喲啊提防這些漏洞。
1. 類型
WebView中,主要漏洞有三類:
任意代碼執行漏洞 密碼明文存儲漏洞 域控制不嚴格漏洞
2. 具體分析
2.1 WebView 任意代碼執行漏洞
出現該漏洞的原因有三個:
WebView 中 addJavascriptInterface() 接口 WebView 內置導出的 searchBoxJavaBridge_對象 WebView 內置導出的 accessibility 和 accessibilityTraversalObject 對象
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 協議的文件
Paste_Image.png
解決方案:
對於不需要使用 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代碼為:
<script> function loadXMLDoc() { var arm = "file:///etc/hosts"; var xmlhttp; if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } xmlhttp.onreadystatechange=function() { //alert("status is"+xmlhttp.status); if (xmlhttp.readyState==4) { console.log(xmlhttp.responseText); } } xmlhttp.open("GET",arm); xmlhttp.send(null); } loadXMLDoc(); </script> // 通過該代碼可成功讀取 /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代碼是:
// 通過該代碼可成功讀取 https://www.so.com 的內容 <script> function loadXMLDoc() { var arm = "https://www.so.com"; var xmlhttp; if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } xmlhttp.onreadystatechange=function() { //alert("status is"+xmlhttp.status); if (xmlhttp.readyState==4) { console.log(xmlhttp.responseText); } } xmlhttp.open("GET",arm); xmlhttp.send(null); } loadXMLDoc(); </script>
解決方案:設置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); }