android WebView详解,常见漏洞详解和安全源码(上)
這篇博客主要來介紹 WebView 的相關使用方法,常見的幾個漏洞,開發中可能遇到的坑和最后解決相應漏洞的源碼,以及針對該源碼的解析。?
由于博客內容長度,這次將分為上下兩篇,上篇詳解 WebView 的使用,下篇講述 WebView 的漏洞和坑,以及修復源碼的解析。?
下篇:android WebView詳解,常見漏洞詳解和安全源碼(下)?
轉載請注明出處:http://blog.csdn.net/self_study/article/details/54928371。?
對技術感興趣的同鞋加群 544645972 一起交流。
Android Hybrid 和 WebView 解析
現在市面上的 APP 根據類型大致可以分為 3 類:Native APP、Web APP 和 Hybrid APP,而 Hybrid APP 兼具 “Native APP 良好用戶交互體驗的優勢”和 “Web APP 跨平臺開發的優勢”,現在很多的主流應用也是使用 Hybrid 模式開發的。
Hybrid 的優勢與原生的體驗差距
Hybrid 的優勢
為什么要使用 Hybrid 開發呢,這就要提到 native 開發的限制:
1.客戶端發板周期長
眾所周知,客戶端的發板周期在正常情況下比較長,就算是創業公司的迭代也在一到兩個星期一次,大公司的迭代周期一般都在月這個數量級別上,而且 Android 還好,iOS 的審核就算變短了也有幾天,而且可能會有審核不通過的意外情況出現,所謂為了應對業務的快速發展,很多業務比如一些活動頁面就可以使用 H5 來進行開發。
2.客戶端大小體積受限
如果所有的東西都使用 native 開發,比如上面提到的活動頁面,就會造成大量的資源文件要加入到 APK 中,這就造成 APK 大小增加,而且有的活動頁面更新很快,造成資源文件可能只會使用一個版本,如果不及時清理,就會造成資源文件的殘留。
3.web 頁面的體驗問題
使用純 Web 開發,比以前迭代快速很多,但是從某種程度上來說,還是不如原生頁面的交互體驗好;?
4.無法跨平臺
一般情況下,同一樣的頁面在 android 和 iOS 上需要寫兩份不同的代碼,但是現在只需要寫一份即可,Hybrid 具有跨平臺的優勢。
所以綜上這兩種方式單獨處理都不是特別好,考慮到發版周期不定,而且體驗交互上也不能很差,所以就把兩種方式綜合起來,讓終端和前端共同開發一個 APP,這樣一些迭代很穩定的頁面就可以使用原生,增加體驗性;一些迭代很快速的頁面就可以使用 H5,讓兩種優點結合起來,彌補原來單個開發模式的缺點。?
H5 與 Native 的體驗差距
H5 和 Native 的體驗差距主要在兩個方面:
1.頁面渲染瓶頸
第一個是前端頁面代碼渲染,受限于 JS 的解析效率,以及手機硬件設備的一些性能,所以從這個角度來說,我們應用開發者是很難從根本上解決這個問題的;
2.資源加載緩慢
第二個方面是 H5 頁面是從服務器上下發的,客戶端的頁面在內存里面,在頁面加載時間上面,根據網絡狀況的不同,H5 頁面的體驗和 Native 在很多情況下相比差距還是不小的,但是這種問題從某種程度上來說也是可以彌補的,比如說我們可以做一些資源預加載的方案,在資源預加載方面,其實也有很多種方式,下面主要列舉了一些:
- 第一種方式是使用 WebView 自身的緩存機制:如果我們在 APP 里面訪問一個頁面,短時間內再次訪問這個頁面的時候,就會感覺到第二次打開的時候順暢很多,加載速度比第一次的時間要短,這個就是因為 WebView 自身內部會做一些緩存,只要打開過的資源,他都會試著緩存到本地,第二次需要訪問的時候他直接從本地讀取,但是這個讀取其實是不太穩定的東西,關掉之后,或者說這種緩存失效之后,系統會自動把它清除,我們沒辦法進行控制。基于這個 WebView 自身的緩存,有一種資源預加載的方案就是,我們在應用啟動的時候可以開一個像素的 WebView ,事先去訪問一下我們常用的資源,后續打開頁面的時候如果再用到這些資源他就可以從本地獲取到,頁面加載的時間會短一些。
- 第二種方案是,我們自己去構建,自己管理緩存:把這些需要預加載的資源放在 APP 里面,他可能是預先放進去的,也可能是后續下載的,問題在于前端這些頁面怎么去緩存,兩個方案,第一種是前端可以在 H5 打包的時候把里面的資源 URL 進行替換,這樣可以直接訪問本地的地址;第二種是客戶端可以攔截這些網頁發出的所有請求做替換:
這個是美團使用的預加載方案(詳情請看:
WebView 詳細介紹
我們來看看 Google 官網關于 WebView 的介紹:
A View that displays web pages. This class is the basis upon which you can roll your own web browseror simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.- 1
- 2
- 3
- 4
可以看到 WebView 是一個顯示網頁的控件,并且可以簡單的顯示一些在線的內容,并且基于 WebKit 內核,在 Android4.4(API Level 19) 引入了一個基于 Chromium 的新版本 WebView ,這讓我們的 WebView 能支持 HTML5 和 CSS3 以及 Javascript,有一點需要注意的是由于 WebView 的升級,對于我們的程序也帶來了一些影響,如果我們的 targetSdkVersion 設置的是 18 或者更低, single and narrow column 和 default zoom levels 不再支持。Android4.4 之后有一個特別方便的地方是可以通過 setWebContentDebuggingEnabled() 方法讓我們的程序可以進行遠程桌面調試。
WebView 加載頁面
WebView 有四個用來加載頁面的方法:
- loadUrl (String url)
- loadUrl (String url, Map<String, String> additionalHttpHeaders)
- loadData(String data, String mimeType, String encoding)
- loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
WebView 常見設置
使用 WebView 的時候,一般都會對其進行一些設置,我們來看看常見的設置:
WebSettings webSettings = webView.getSettings(); //設置了這個屬性后我們才能在 WebView 里與我們的 Js 代碼進行交互,對于 WebApp 是非常重要的,默認是 false, //因此我們需要設置為 true,這個本身會有漏洞,具體的下面我會講到 webSettings.setJavaScriptEnabled(true);//設置 JS 是否可以打開 WebView 新窗口 webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//WebView 是否支持多窗口,如果設置為 true,需要重寫 //WebChromeClient#onCreateWindow(WebView, boolean, boolean, Message) 函數,默認為 false webSettings.setSupportMultipleWindows(true);//這個屬性用來設置 WebView 是否能夠加載圖片資源,需要注意的是,這個方法會控制所有圖片,包括那些使用 data URI 協議嵌入 //的圖片。使用 setBlockNetworkImage(boolean) 方法來控制僅僅加載使用網絡 URI 協議的圖片。需要提到的一點是如果這 //個設置從 false 變為 true 之后,所有被內容引用的正在顯示的 WebView 圖片資源都會自動加載,該標識默認值為 true。 webSettings.setLoadsImagesAutomatically(false); //標識是否加載網絡上的圖片(使用 http 或者 https 域名的資源),需要注意的是如果 getLoadsImagesAutomatically() //不返回 true,這個標識將沒有作用。這個標識和上面的標識會互相影響。 webSettings.setBlockNetworkImage(true);//顯示WebView提供的縮放控件 webSettings.setDisplayZoomControls(true); webSettings.setBuiltInZoomControls(true);//設置是否啟動 WebView API,默認值為 false webSettings.setDatabaseEnabled(true);//打開 WebView 的 storage 功能,這樣 JS 的 localStorage,sessionStorage 對象才可以使用 webSettings.setDomStorageEnabled(true);//打開 WebView 的 LBS 功能,這樣 JS 的 geolocation 對象才可以使用 webSettings.setGeolocationEnabled(true); webSettings.setGeolocationDatabasePath("");//設置是否打開 WebView 表單數據的保存功能 webSettings.setSaveFormData(true);//設置 WebView 的默認 userAgent 字符串 webSettings.setUserAgentString("");//設置是否 WebView 支持 “viewport” 的 HTML meta tag,這個標識是用來屏幕自適應的,當這個標識設置為 false 時, //頁面布局的寬度被一直設置為 CSS 中控制的 WebView 的寬度;如果設置為 true 并且頁面含有 viewport meta tag,那么 //被這個 tag 聲明的寬度將會被使用,如果頁面沒有這個 tag 或者沒有提供一個寬度,那么一個寬型 viewport 將會被使用。 webSettings.setUseWideViewPort(false);//設置 WebView 的字體,可以通過這個函數,改變 WebView 的字體,默認字體為 "sans-serif" webSettings.setStandardFontFamily(""); //設置 WebView 字體的大小,默認大小為 16 webSettings.setDefaultFontSize(20); //設置 WebView 支持的最小字體大小,默認為 8 webSettings.setMinimumFontSize(12);//設置頁面是否支持縮放 webSettings.setSupportZoom(true); //設置文本的縮放倍數,默認為 100 webSettings.setTextZoom(2);- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
然后還有最常用的 WebViewClient 和 WebChromeClient,WebViewClient主要輔助WebView執行處理各種響應請求事件的,比如:?
- onLoadResource
- onPageStart
- onPageFinish
- onReceiveError
- onReceivedHttpAuthRequest
- shouldOverrideUrlLoading
- onCloseWindow(關閉WebView)
- onCreateWindow
- onJsAlert
- onJsPrompt
- onJsConfirm
- onProgressChanged
- onReceivedIcon
- onReceivedTitle
- onShowCustomView
接著還有 WebView 的幾種緩存模式:
- LOAD_CACHE_ONLY不使用網絡,只讀取本地緩存數據;
- LOAD_DEFAULT根據 cache-control 決定是否從網絡上取數據;
- LOAD_CACHE_NORMALAPI level 17 中已經廢棄, 從 API level 11 開始作用同 LOAD_DEFAULT 模式 ;
- LOAD_NO_CACHE不使用緩存,只從網絡獲取數據;
- LOAD_CACHE_ELSE_NETWORK只要本地有,無論是否過期,或者 no-cache,都使用緩存中的數據。
清空緩存和清空歷史記錄,CacheManager 來處理 webview 緩存相關:mWebView.clearCache(true);;清空歷史記錄mWebview.clearHistory();,這個方法要在?onPageFinished()?的方法之后調用。
WebView 與 native 的交互
使用 Hybrid 開發的 APP 基本都需要 Native 和 web 頁面的 JS 進行交互,下面介紹一下交互的方式。
js 調用 native
如何讓 web 頁面調用 native 的代碼呢,有三種方式:
第一種方式:通過 addJavascriptInterface 方法進行添加對象映射?
這種是使用最多的方式了,首先第一步我們需要設置一個屬性:
- 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
可以看到,這種方式的好處在于使用簡單明了,本地和 JS 的約定也很簡單,就是對象名稱和方法名稱約定好即可,缺點就是下面要提到的漏洞問題。
第二種方式:利用 WebViewClient 接口回調方法攔截 url
這種方式其實實現也很簡單,使用的頻次也很高,上面我們介紹到了 WebViewClient ,其中有個回調接口?shouldOverrideUrlLoading (WebView view, String url)?,我們就是利用這個攔截 url,然后解析這個 url 的協議,如果發現是我們預先約定好的協議就開始解析參數,執行相應的邏輯,我們先來看看這個函數的介紹:
- 1
- 2
- 3
- 4
- 5
注意這個方法在 API24 版本已經廢棄了,需要使用?shouldOverrideUrlLoading (WebView view, WebResourceRequest request)?替代,使用方法很類似,我們這里就使用?shouldOverrideUrlLoading (WebView view, String url)?方法來介紹一下:
public boolean shouldOverrideUrlLoading(WebView view, String url) {//假定傳入進來的 url = "js://openActivity?arg1=111&arg2=222",代表需要打開本地頁面,并且帶入相應的參數Uri uri = Uri.parse(url);String scheme = uri.getScheme();//如果 scheme 為 js,代表為預先約定的 js 協議if (scheme.equals("js")) {//如果 authority 為 openActivity,代表 web 需要打開一個本地的頁面if (uri.getAuthority().equals("openActivity")) {//解析 web 頁面帶過來的相關參數HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();for (String name : collection) {params.put(name, uri.getQueryParameter(name));}Intent intent = new Intent(getContext(), MainActivity.class);intent.putExtra("params", params);getContext().startActivity(intent);}//代表應用內部處理完成return true;}return super.shouldOverrideUrlLoading(view, url); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 1
- 2
- 1
- 2
- 3
- 4
所以說第二種方式在返回值方面還是很繁瑣的,但是在不需要返回值的情況下,比如打開 Native 頁面,還是很合適的,制定好相應的協議,就能夠讓 web 端具有打開所有本地頁面的能力了。
第三種方式:利用 WebChromeClient 回調接口的三個方法攔截消息
這個方法的原理和第二種方式原理一樣,都是攔截相關接口,只是攔截的接口不一樣:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- onJsAlert 方法是彈出警告框,一般情況下在 Android 中為 Toast,在文本里面加入\n就可以換行;
- onJsConfirm 彈出確認框,會返回布爾值,通過這個值可以判斷點擊時確認還是取消,true表示點擊了確認,false表示點擊了取消;
- onJsPrompt 彈出輸入框,點擊確認返回輸入框中的值,點擊取消返回 null。
- 1
- 2
- 3
- 4
這里需要注意的是 prompt 里面的內容是通過 message 傳遞過來的,并不是第二個參數的 url,返回值是通過 JsPromptResult 對象傳遞。為什么要攔截 onJsPrompt 方法,而不是攔截其他的兩個方法,這個從某種意義上來說都是可行的,但是如果需要返回值給 web 端的話就不行了,因為 onJsAlert 是不能返回值的,而 onJsConfirm 只能夠返回確定或者取消兩個值,只有 onJsPrompt 方法是可以返回字符串類型的值,操作最全面方便。
以上三種方案的總結和對比?
以上三種方案都是可行的,在這里總結一下
- 第一種方式:是現在目前最普遍的用法,方便簡潔,但是唯一的不足是在 4.2 系統以下存在漏洞問題;
- 第二種方式:通過攔截 url 并解析,如果是已經約定好的協議則進行相應規定好的操作,缺點就是協議的約束需要記錄一個規范的文檔,而且從 Native 層往 Web 層傳遞值比較繁瑣,優點就是不會存在漏洞,iOS7 之下的版本就是使用的這種方式。
- 第三種方式:和第二種方式的思想其實是類似的,只是攔截的方法變了,這里攔截了 JS 中的三種對話框方法,而這三種對話框方法的區別就在于返回值問題,alert 對話框沒有返回值,confirm 的對話框方法只有兩種狀態的返回值,prompt 對話框方法可以返回任意類型的返回值,缺點就是協議的制定比較麻煩,需要記錄詳細的文檔,但是不會存在第二種方法的漏洞問題。
native 調用 js
第一種方式?
native 調用 js 的方法上面已經介紹到了,方法為:
- 1
- 2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
需要注意的是名字一定要對應上,要不然是調用不成功的,而且還有一點是?JS 的調用一定要在 onPageFinished 函數回調之后才能調用,要不然也是會失敗的。?
第二種方式?
如果現在有需求,我們要得到一個 Native 調用 Web 的回調怎么辦,Google 在 Android4.4 為我們新增加了一個新方法,這個方法比 loadUrl 方法更加方便簡潔,而且比 loadUrl 效率更高,因為 loadUrl 的執行會造成頁面刷新一次,這個方法不會,因為這個方法是在 4.4 版本才引入的,所以我們使用的時候需要添加版本的判斷:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
兩種方式的對比?
一般最常使用的就是第一種方法,但是第一種方法獲取返回的值比較麻煩,而第二種方法由于是在 4.4 版本引入的,所以局限性比較大。
WebView 常見漏洞和坑
常見漏洞和坑請看下篇博客:android WebView詳解,常見漏洞詳解和安全源碼(下)
源碼
源碼解析請看下篇博客:android WebView詳解,常見漏洞詳解和安全源碼(下)?
下載源碼:https://github.com/zhaozepeng/SafeWebView;參考自:https://github.com/yushiwo/WebViewBugDemo,在此基礎上做了一些優化。?
轉載請注明出處:http://blog.csdn.net/self_study/article/details/54928371。
引用
http://group.jobbole.com/26417/?utm_source=android.jobbole.com&utm_medium=sidebar-group-topic?
http://blog.csdn.net/jiangwei0910410003/article/details/52687530?
http://blog.csdn.net/leehong2005/article/details/11808557?
https://github.com/yushiwo/WebViewBugDemo/blob/master/src/com/lee/webviewbug/WebViewEx.java?
http://blog.csdn.net/sk719887916/article/details/52402470?
https://zhuanlan.zhihu.com/p/24202408?
https://github.com/lzyzsd/JsBridge?
http://www.jianshu.com/p/93cea79a2443#?
http://www.codexiu.cn/android/blog/33214/?
https://github.com/pedant/safe-java-js-webview-bridge?
http://blog.sina.com.cn/s/blog_777f9dbb0102v8by.html?
http://www.cnblogs.com/chaoyuehedy/p/5556557.html?
http://blogs.360.cn/360mobile/2014/09/22/webview%E8%B7%A8%E6%BA%90%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/
https://my.oschina.net/zhibuji/blog/100580?
http://www.cnblogs.com/punkisnotdead/p/5062631.html?utm_source=tuicool&utm_medium=referral
版權聲明:轉載請標明出處http://blog.csdn.net/self_study,對技術感興趣的同鞋加群544645972一起交流 http://blog.csdn.net/zhao_zepeng/article/details/54928371
總結
以上是生活随笔為你收集整理的android WebView详解,常见漏洞详解和安全源码(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Create your own bloc
- 下一篇: android WebView详解,常见