我们希望使用股票Android WebView作为沙箱来执行本地HTML / JS应用程序 . 主要的安全性要求是将WebView设置为完全脱机,并且只允许调用某些javascript接口 . 使用WebView.addJavascriptInterface()方法将这些接口传递到javascript运行时 .
Android应用程序本身具有访问网络的权限(android.permission.INTERNET) .
我可以禁用正常的http / https请求, but totally failed in blocking WebSocket requests . 看起来这些处理与普通的http请求不同 .
一种替代方法是覆盖WebSocket JavaScript方法 . 但是这让我感觉不好,因为它违背了沙箱的概念 . 似乎也可以使用delete来恢复原始函数指针 .
另一种选择是将自己的自定义WebView(例如Crosswalk-Project)与我们的应用程序捆绑在一起,但是由于编译和更新是相当费力的,所以要避免使用它 .
我尝试了以下公共WebView接口,但它们似乎都没有阻止WebSocket调用:
-
webSettings.setBlockNetworkLoads(true);
-
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
-
webView.setNetworkAvailable(false);
-
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 回答
不幸的是,目前无法在WebView中拦截WebSockets调用 . 您只能阻止整个JavaScript执行或网络访问 . 你是正确的,尝试使用自己的实现覆盖
WebSocket
构造函数可以通过删除重写的窗口属性轻松解决,因此可以从window
原型链访问的内置构造函数再次可见 . 如果您尝试使用WebView.addJavascriptInterface
覆盖WebSocket
,则会发生相同的情况 .我认为,HTML应用程序缓存也没有被WebView回调拦截,它甚至不需要打开Javascript才能工作 .
我想,您使用系统WebView创建WebView网络沙箱的唯一机会是使用没有网络访问权限的专用应用程序,并使用Binder IPC与其进行通信 .
捆绑自定义WebView会带来更多困难,因为您需要查找并插入所有漏洞,然后继续使用自己的安全修复程序进行更新 .
另一种可能的方法是在加载到webview中的页面源中包含内容安全策略:
有了这个,我发现构造
echo.websocket.org
的websocket实例只会抛出一个错误并将CSP违规记录到控制台 .或者,您可以在
ShouldInterceptRequest
方法中设置此标头;这是一个简化的例子:请注意,对
file:///android_asset/...
和file:///android_res/...
地址的请求不会触发拦截方法,但其他file:///...
地址会触发 .另请注意,我仅在Android 8.0上测试过此技术 . 显然Chrome自2013年以来一直支持CSP,但我不能肯定这对Android的实施意味着什么 .
关于CSP的主题,可能值得为离线页面设置更全面的策略 . 假设原点是
file://
,可能是这样的: