Aug 31

超级JavascriptInterface

Lrdcq , 2015/08/31 21:03 , 程序 , 閱讀(5652) , Via 本站原創
大家都知道,在android的webview中,有非常非常严重的bug。
那就是4.2以下的webview的JavascriptInterface可以访问任何java本地代码,以至于程序完全被劫持。
更惨的是,android旧版本自带一个searchBoxJavaBridge_的接口,导致漏洞直接暴露在互联网中。

查看乌云上近年来的记录,大到微信,小到快车,无数app都经历过次劫。
android4.2以上修复了这个bug,并提供了更安全的解决方案:在提供的方法前添加注解:@ JavascriptInterface
因此,我们需要一个合理的解决方案。

当然,方法很多,比较推荐的是微信采用的jsbridge方案(参看解包微信的源码,js代码并没有混淆)。
然而对于安卓开发者,我想追求一种更优雅的,可以和4.2以上官方方案自然衔接的方案。



于是我编写库:ExtendJavascriptInterface(https://github.com/Lrdcq/ExtendJavascriptInterface/

首先请看它的使用方法:
首先我们以4.2以上版本的方法定义js接口:
   public class JsBridge {
        @JavascriptInterface
        public String send(String str) {
            wvBox.loadUrl("javascript:do2('-"+str+"-')");
            return "javagot:" + str;
        }

        @JavascriptInterface
        public void toast(String message) {
            Toast.makeText(wvBox.getContext(), message, Toast.LENGTH_SHORT).show();
        }
    }

挺好的,所以我希望旧版用相同的东西就可以了,因此旧版也用相同的方法定义接口。
其次,怎么使用呢,简单来说看起来会是这样:
        JsBridge bridge = new JsBridge();

        wvBox = (WebView) findViewById(R.id.webview);
        wvBox.removeJavascriptInterface("searchBoxJavaBridge_");
        wvBox.loadUrl(fromUrl());

        //
        wvBox.getSettings().setJavaScriptEnabled(true);
        //!!!!!!!!!!!!!!!!!!!!!!使用方法!!!!!!!!!!!!!!!!!
        if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN_MR1) {//4.2以下危险webview的使用本方法
            ExtendJavascriptInterface<JsBridge> t=new ExtendJavascriptInterface(bridge,bridgeName);
            wvBox.setWebChromeClient(new ExtendJavascriptInterfaceClient(t));
        }else{//正常用法
            wvBox.addJavascriptInterface(bridge,bridgeName);
            wvBox.setWebChromeClient(new WebChromeClient());
        }
  //!!!!!!!!!!!!!!!!!!!!!使用方法结束!!!!!!!!!!!!!!

主要的区别集中在webview怎样添加JavascriptInterface和setWebChromeClient上,如果是4.2以上,一切安好;如果是旧版本,就用这段代码提供的方法咯。
另外记得remove掉searchBoxJavaBridge_,仅此而已。
看起来很简单吧。

那么这个方法的内部原理是怎样的呢?

1.首先,我们用反射注解把拥有@JavascriptInterface注解的方法数据化,并且把这些方法用数据化的方式暂存起来。
2.然后,webview也加载好了,我们用bridge的方法的数据构成一段js,用loadurl把js直接注入网页中。
3.这段js做的事情是,在js中注入与bridge方法名相同的全局函数或者方法,然后就可以表示bridge加载完成,js可以使用这些方法了
4.当在js使用这些方法的时候,我们重写了在移动开发中几乎用不到的alert函数,js出发alert,按一定格式把数据json化发出去。
5.android重写onJsAlert然后截取到发过来的数据,可以看到是名字叫什么的函数,参数是些什么。
6.最后,找到之前解释注解储存的方法,对应的找出来,invoke出来~即可~
7.同时,我们也考虑提供回调方法,当通信完成后产生回调事件并触发。

这样,绕过自带的JsBridge并实现看起来像自带的JsBridge的功能就完成了。
logo