天天看點

WebView深度學習(三)之WebView的記憶體洩漏、漏洞以及緩存機制原理和解決方案

上兩篇文章講到了 WebView的基本使用以及Android和js的互動 以及 全面總結WebView遇到的坑及優化 ,這篇文章講一下記憶體洩漏和漏洞處理。如果你想更深入的了解WebView,這篇文章值得一看。

⇒ 六、WebView的記憶體洩漏怎麼辦?

1.不在xml中定義 Webview ,而是在需要的時候在Activity中建立,并且Context使用 getApplicationgContext()
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
                                         ViewGroup.LayoutParams.MATCH_PARENT);
    mWebView = new WebView(getApplicationContext());
    mWebView.setLayoutParams(params);
    mLayout.addView(mWebView);
           
2.在 Activity 銷毀( WebView )的時候,先讓 WebView 加載null内容,然後移除 WebView,再銷毀 WebView,最後置空。
@Override
protected void onDestroy() {
    if (mWebView != null) {
        mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
        mWebView.clearHistory();

        ((ViewGroup) mWebView.getParent()).removeView(mWebView);
        mWebView.destroy();
        mWebView = null;
    }
    super.onDestroy();
}
           

⇒ 七、WebView的使用漏洞 及其修複方式

WebView中,主要漏洞有三類:

1.任意代碼執行漏洞
2.密碼明文存儲漏洞
3.域控制不嚴格漏洞
           
(一)任意代碼執行漏洞
  • (1)addJavascriptInterface 接口引起遠端代碼執行漏洞

1. 漏洞産生原因:

js調用Android的其中一個方式是通過addJavascriptInterface接口進行對象映射:

    webView.addJavascriptInterface(new JSObject(), "myObj");
    // 參數1:Android的本地對象
    // 參數2:JS的對象
    // 通過對象映射将Android中的本地對象和JS中的對象進行關聯,進而實作JS調用Android的對象和方法
    是以,漏洞産生原因是:當JS拿到android這個對象後,就可以調用這個Android對象中所有的方法,包括系統類(Java.lang.Runtime 類),
進而進行任意代碼執行。(比如**我們可以執行指令擷取本地裝置的SD卡中的檔案等資訊進而造成資訊洩露**)
           

具體擷取系統類的描述:(結合 Java 反射機制)

    1. Android中的對象有一公共的方法:getClass() ;
    1. 該方法可以擷取到目前類 類型Class
    1. 該類有一關鍵的方法: Class.forName;
    1. 該方法可以加載一個類(可加載 java.lang.Runtime 類)
    1. 而該類是可以執行本地指令的

以下是攻擊的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 代碼進行漏洞攻擊。在微信盛行、掃一掃行為普及的情況下,該漏洞的危險性非常大

2.解決方法

Android 4.2版本之後:Google 在Android 4.2 版本中規定對被調用的函數以 @JavascriptInterface進行注解進而避免漏洞攻擊

Android 4.2版本之前:采用攔截prompt()進行漏洞修複。

具體步驟如下:

1.繼承 WebView ,重寫 addJavascriptInterface 方法,然後在内部自己維護一個對象映射關系的 Map ( 将需要添加的 JS 接口放入該Map中 )
2.每次當 WebView 加載頁面前加載一段本地的 JS 代碼,原理是:
    1) 讓JS調用一Javascript方法:該方法是通過調用prompt()把JS中的資訊(含特定辨別,方法名稱等)傳遞到Android端;
    2) 在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對象的方法
           

關于采用攔截prompt()進行漏洞修複需要注意的兩點細節:

細節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()
           
  • (2)searchBoxJavaBridge_接口引起遠端代碼執行漏洞

1. 産生原因

1) 在Android 3.0以下,Android系統會預設通過searchBoxJavaBridge_的Js接口給 WebView 添加一個JS映射對象:
searchBoxJavaBridge_對象
2) 該接口可能被利用,實作遠端任意代碼。
           

2. 解決方法

删除searchBoxJavaBridge_接口
// 通過調用該方法删除接口removeJavascriptInterface();
           
  • (3)accessibility和 accessibilityTraversal接口引起遠端代碼執行漏洞
1) 在Android 3.0以下,Android系統會預設通過searchBoxJavaBridge_的Js接口給 WebView 添加一個JS映射對象:
searchBoxJavaBridge_對象
2) 該接口可能被利用,實作遠端任意代碼。
           
删除searchBoxJavaBridge_接口
// 通過調用該方法删除接口removeJavascriptInterface();
           
(二)密碼明文存儲漏洞
  • (1)問題分析
//WebView預設開啟密碼儲存功能 :
mWebView.setSavePassword(true)
開啟後,在使用者輸入密碼時,會彈出提示框:詢問使用者是否儲存密碼;
如果選擇”是”,密碼會被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險
           
  • (2)解決方案
//關閉密碼儲存提醒
WebSettings.setSavePassword(false) 
           
(三)域控制不嚴格漏洞

先看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"
           
上述demo中:即 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()
           
  • (2) setAllowFileAccess()
// 設定是否允許 WebView 使用 File 協定,預設設定為true,即允許在 File 域下執行任意 JavaScript 代碼
webView.getSettings().setAllowFileAccess(true);     
           

但是同時也限制了 WebView 的功能,使其不能加載本地的 html 檔案,( 移動版的 Chrome 預設禁止加載 file 協定的檔案 ) ,如下圖:

解決方案:

1) 對于不需要使用 file 協定的應用,禁用 file 協定;
  setAllowFileAccess(false); 
  
2) 對于需要使用 file 協定的應用,禁止 file 協定加載 JavaScript。
  setAllowFileAccess(true); 
  // 禁止 file 協定加載 JavaScript
  if (url.startsWith("file://") {
      setJavaScriptEnabled(false);
  } else {
      setJavaScriptEnabled(true);
  }
           
  • (3)setAllowFileAccessFromFileURLs()

設定是否允許通過 file url 加載的 Js代碼讀取其他的本地檔案 , 在Android 4.1前預設允許 , 在Android 4.1後預設禁止

webView.getSettings().setAllowFileAccessFromFileURLs(true);
           

當AllowFileAccessFromFileURLs()設定為 true 時,攻擊者的JS代碼為 ( 通過該代碼可成功讀取 /etc/hosts 的内容資料 ) :

<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>
           
設定setAllowFileAccessFromFileURLs(false);
           

當設定成為 false 時,上述JS的攻擊代碼執行會導緻錯誤,表示浏覽器禁止從 file url 中的

JavaScript

讀取其它本地檔案。

  • (4) setAllowUniversalAccessFromFileURLs()

設定是否允許通過 file url 加載的 Javascript 可以通路其他的源(包括http、https等源),在Android 4.1前預設允許(setAllowFileAccessFromFileURLs()不起作用),在Android 4.1後預設禁止

webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
           

當AllowFileAccessFromFileURLs()被設定成true時,攻擊者的JS代碼是:

// 通過該代碼可成功讀取 http://www.so.com 的内容
<script>
    function loadXMLDoc(){
        var arm = "http://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);
           
  • (5) setJavaScriptEnabled()

設定是否允許 WebView 使用 JavaScript(預設是不允許),但很多應用(包括移動浏覽器)為了讓 WebView 執行 http 協定中的 JavaScript,都會主動設定為true,不差別對待是非常危險的,如下代碼所示:

webView.getSettings().setJavaScriptEnabled(true);  
           

即使把setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()都設定為 false,通過 file URL 加載的 javascript仍然有方法通路其他的本地檔案:符号連結跨源攻擊(前提是允許 file URL 執行 javascript,即webView.getSettings().setJavaScriptEnabled(true);)

原因分析:

這一攻擊能奏效的原因是:通過 javascript 的延時執行和将目前檔案替換成指向其它檔案的軟連結就可以讀取到被符号連結所指的檔案。
           

具體攻擊步驟:(在該指令執行前 xx.html 是不存在的;執行完這條指令之後,就生成了這個檔案,并且将 Cookie 檔案連結到了 xx.html 上。)

1. 把惡意的 js 代碼輸出到攻擊應用的目錄下,随機命名為 xx.html,修改該目錄的權限;

2. 修改後休眠 1s,讓檔案操作完成;

3. 完成後通過系統的 Chrome 應用去打開該 xx.html 檔案

4. 等待 4s 讓 Chrome 加載完成該 html,最後将該 html 删除,并且使用 ln -s 指令為 Chrome 的 Cookie 檔案建立軟連接配接,

于是就可通過連結來通路 Chrome 的 Cookie

注意事項:

  Google 沒有進行修複,隻是讓Chrome 最新版本預設禁用 file 協定,是以這一漏洞在最新版的 Chrome 中并不存在。

  但是,在日常大量使用 WebView 的App和浏覽器,都有可能受到此漏洞的影響。通過利用此漏洞,容易出現資料洩露的危險

  如果是 file 協定,禁用 javascript 可以很大程度上減小跨源漏洞對 WebView 的威脅。

  但并不能完全杜絕跨源檔案洩露。例:應用實作了下載下傳功能,對于無法加載的頁面,會自動下載下傳到 sd 卡中;由于 sd 卡中的檔案所有應用都可以通路,于是可以通過構造一個 file URL 指向被攻擊應用的私有檔案,然後用此 URL 啟動被攻擊應用的 WebActivity,這樣由于該 WebActivity 無法加載該檔案,就會将該檔案下載下傳到 sd 卡下面,然後就可以從 sd 卡上讀取這個檔案了

  • (6) 最終解決方案

1)對于不需要使用 file 協定的應用,禁用 file 協定;

// 禁用 file 協定;
webView.setAllowFileAccess(false); 
webView.setAllowFileAccessFromFileURLs(false);
webView.setAllowUniversalAccessFromFileURLs(false);
           

2)對于需要使用 file 協定的應用,禁止 file 協定加載 JavaScript。

// 需要使用 file 協定
webView.setAllowFileAccess(true); 
webView.setAllowFileAccessFromFileURLs(false);
webView. setAllowUniversalAccessFromFileURLs(false);

// 禁止 file 協定加載 JavaScript
if (url.startsWith("file://") {
    webView.setJavaScriptEnabled(false);
} else {
    webView.setJavaScriptEnabled(true);
}
           

⇒ 八、WebView 的緩存機制 & 資源預加載方案

參考文章:

http://blog.csdn.net/carson_ho/article/details/71402764