首页 文章

强制Android WebView脱机

提问于
浏览
5

我们希望使用股票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 回答

  • 0

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

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

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

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

  • 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:;
    

相关问题