强制 Android WebView 离线

Force 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)与我们的应用程序捆绑在一起,但希望避免它,因为编译和更新非常费力。

我尝试了以下 public WebView 接口,但其中 none 似乎阻止了 WebSocket 调用:

在 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);
};

知道如何做到这一点吗?

非常感谢

很遗憾,目前还没有办法在 WebView 中拦截 WebSockets 调用。您只能阻止整个 JavaScript 执行或网络访问。您是正确的,通过删除覆盖的 window 属性 可以轻松解决尝试使用您自己的实现覆盖 WebSocket 构造函数的问题,因此可以从 [=11= 访问内置构造函数] 原型链再次可见。如果您尝试用 WebView.addJavascriptInterface.

覆盖 WebSocket,也会发生同样的情况

我认为,HTML Application Cache 也不会被 WebView 回调拦截,甚至不需要打开 Javascript 即可工作。

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

与其通信

捆绑自定义的 WebView 会带来更多困难,因为您需要找出并堵上所有漏洞,然后自己用安全修复程序不断更新它。

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

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

有了这个,我发现为 echo.websocket.org 构建 websocket 实例只会抛出错误并将 CSP 违规记录到控制台。

或者您可以在 ShouldInterceptRequest 方法中设置此 header;这是一个简化的例子:

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