强制Android WebView脱机

时间:2015-05-19 12:37:05

标签: javascript android webview offline

我们希望使用股票Android WebView作为沙箱来执行本地HTML / JS应用程序。主要的安全性要求是将WebView设置为完全脱机,并且只允许调用某些javascript接口。这些接口使用WebView.addJavascriptInterface()方法传递到javascript运行时。

Android应用程序本身具有访问网络的权限(android.permission.INTERNET)。

我能够禁用正常的http / https请求,但完全无法阻止WebSocket请求。似乎这些处理与普通的http请求不同。

另一种方法是覆盖WebSocket JavaScript方法。但是这让我感觉不好,因为它违背了沙盒的概念。似乎也可以使用delete来恢复原始函数指针。

另一种选择是将自己的自定义WebView(例如Crosswalk-Project)与我们的应用程序捆绑在一起,但是希望避免使用它,因为编译和更新是相当费力的。

我尝试了以下公共WebView接口,但它们似乎都没有阻止WebSocket调用:

  • webSettings.setBlockNetworkLoads(真);
  • webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
  • webView.setNetworkAvailable(假);
  • WebViewClient.shouldOverrideUrlLoading()(回调)
  • WebViewClient.shouldInterceptRequest()(回调,尝试过两个版本)
  • WebChromeClient.onPermissionRequest()

在Android 4.4.4(19)和Android 5 / 5.1(21/22)上测试过。

我执行的javascript:

ws = new WebSocket("wss://echo.websocket.org");

ws.onmessage = function(event) {
  console.log("received: " + event.data);
};

ws.onclose = function() {
  console.log("External Socket closed");
};

ws.onopen = function() {
  console.log("Connected to external ws");
  ws.send("Hello from " + navigator.userAgent);
};

有什么想法可以做到这一点?

非常感谢

2 个答案:

答案 0 :(得分:0)

不幸的是,目前无法在WebView中拦截WebSockets调用。您只能阻止整个JavaScript执行或网络访问。您是正确的,尝试使用您自己的实现覆盖WebSocket构造函数可以通过删除重写的窗口属性轻松解决,因此可以从window原型链访问的内置构造函数再次可见。如果您尝试使用WebSocket覆盖WebView.addJavascriptInterface,也会发生相同的情况。

我认为,HTML应用程序缓存也没有被WebView回调拦截,它甚至不需要打开Javascript才能工作。

我想,您使用系统WebView创建WebView网络沙箱的唯一机会是使用无网络访问权限的专用应用程序,并使用Binder IPC与其进行通信。

捆绑自定义WebView会带来更多困难,因为您需要查找并插入所有漏洞,然后自行更新安全修复程序。

答案 1 :(得分:0)

另一种可能的方法是在加载到webview的页面源中包括内容安全策略:

<meta http-equiv="Content-Security-Policy" content="connect-src 'none';"/>

安装此功能后,我发现为echo.websocket.org构造websocket实例只会引发错误,并将CSP违规记录到控制台。

或者,您可以在ShouldInterceptRequest方法中设置此标头;这是一个简化的示例:

public override WebResourceResponse ShouldInterceptRequest(WebView view, IWebResourceRequest request)
{
    using (var stream = view.Context.Assets.Open("test.html"))
    {
        var resp = new WebResourceResponse("text/html", "UTF-8", stream);
        resp.ResponseHeaders = new Dictionary<string, string>();
        resp.ResponseHeaders.Add("Content-Security-Policy", "connect-src 'none';");
        return resp;
    }
}

请注意,对file:///android_asset/...file:///android_res/...地址的请求不会触发拦截方法,而其他file:///...地址会触发拦截方法。

另外请注意,我仅在Android 8.0上测试了此技术。显然,Chrome自2013年以来就支持CSP,但我不能确定这对Android的实现意味着什么。


关于CSP主题,可能需要为脱机页面设置更全面的策略。假设原点是file://,也许是这样的:

default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: filesystem:;