WebView安全:JavaScript接口漏洞、远程代码执行、文件访问风险、安全配置最佳实践

WebView 是 Android 开发中绕不开的一个组件。说白了,它就是在原生应用里嵌入一个浏览器内核。很多混合开发、H5 页面展示、甚至一些 SDK 的登录页,都离不开它。

但我要说句实话——WebView 也是 Android 安全漏洞的重灾区。我这些年做安全审计,十次里有七八次都能在 WebView 这块找到问题。轻则信息泄露,重则直接被远程代码执行,手机被人拿走控制权。

这一章,我们就来把 WebView 的四个核心安全问题掰开揉碎讲清楚。

1. JavaScript 接口漏洞:addJavascriptInterface 的坑

先看一个最常见的场景。你需要在 H5 页面里调用原生的能力,比如读取本地文件、调用摄像头。Android 提供了 addJavascriptInterface 方法,可以把一个 Java 对象暴露给 JavaScript。

代码写起来很简单:

WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsBridge(), "bridge");

然后 H5 里就能直接调:

window.bridge.readFile("/data/data/com.example/db");

问题出在哪?任何加载到该 WebView 的页面都能调用这个接口。如果页面被 XSS 攻击,或者用户访问了恶意网站,攻击者就能通过这个接口执行任意 Java 方法。

⚠️ 核心风险:在 Android 4.2(API 17)以下,addJavascriptInterface 存在严重反射漏洞。恶意 JS 可以通过反射调用 Java 类的任何方法,包括 Runtime.exec(),直接执行系统命令。

我记得有一次做项目复盘,发现某个金融类 App 的 WebView 加载了用户输入的 URL,还暴露了文件读写接口。攻击者只需要诱导用户点击一个链接,就能把手机里的通讯录、短信全部上传到服务器。嗯,这个漏洞当时被评了高危。

安全做法

  • 最低 API 版本必须 ≥ 17,并在接口方法上添加 @JavascriptInterface 注解。这样只有被注解的方法才会暴露给 JS。
  • 接口方法要做严格的参数校验。比如路径不能包含 ".." 跳转,协议必须是白名单内的。
  • 不要暴露敏感能力。像文件读写、数据库操作、网络请求这些,能不放就不放。
public class JsBridge {
    @JavascriptInterface
    public String getSafeData(String key) {
        // 只允许读取白名单内的 key
        if (!whitelist.contains(key)) return null;
        return dataStore.get(key);
    }
}

2. 远程代码执行:不止是 addJavascriptInterface

很多人以为远程代码执行只跟 addJavascriptInterface 有关。其实不然。

WebView 的远程代码执行还有两条路径:

  • Chrome 内核漏洞:WebView 底层用的是 Chromium,如果系统 WebView 版本过旧,可能存在 V8 引擎的漏洞。攻击者通过精心构造的 JS 代码,可以直接在 App 进程内执行任意代码。
  • setWebViewClient 的 shouldOverrideUrlLoading:如果回调里直接执行了 loadUrl(),且 URL 来自外部输入,攻击者可以用 javascript: 协议注入代码。

我个人的习惯是:所有来自网络的 URL,在传给 WebView 之前,必须经过协议和域名的双重校验。javascript: 协议直接拦截,file: 协议也要谨慎处理。

webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        // 禁止 javascript: 协议
        if (url.startsWith("javascript:")) {
            return true;
        }
        // 只允许 https 和 白名单域名
        if (!url.startsWith("https://") || !isWhitelistedDomain(url)) {
            return true;
        }
        view.loadUrl(url);
        return true;
    }
});

3. 文件访问风险:file:// 协议的双刃剑

WebView 默认允许通过 file:// 协议访问本地文件。这意味着,如果页面里有一段 JS 代码:

var xhr = new XMLHttpRequest();
xhr.open("GET", "file:///data/data/com.example/shared_prefs/prefs.xml", false);
xhr.send();
console.log(xhr.responseText);

就能直接读到其他 App 的私有数据。当然,Android 有沙箱机制,但同一个 App 内的文件是可以被读到的。

我曾经遇到过一个案例:某个 App 把用户的登录 token 存在了 SharedPreferences 里,同时 WebView 开启了文件访问。攻击者只需要在 H5 页面里嵌入一段 Ajax 请求,就能把 token 偷走。

避坑指南:如果 WebView 不需要加载本地文件,直接关掉文件访问权限。

webView.getSettings().setAllowFileAccess(false);
webView.getSettings().setAllowFileAccessFromFileURLs(false);
webView.getSettings().setAllowUniversalAccessFromFileURLs(false);

这三个开关,我建议全部设为 false。除非你非常清楚自己在做什么。

4. 安全配置最佳实践:一个 Checklist

说了这么多风险,我们来总结一套安全配置。每次创建 WebView 时,按这个清单走一遍,基本能挡住 90% 的攻击。

配置项 推荐值 说明
setJavaScriptEnabled 按需开启 不需要 JS 时务必关闭
setAllowFileAccess false 禁止 file:// 协议
setAllowFileAccessFromFileURLs false 禁止 file 域访问其他文件
setAllowUniversalAccessFromFileURLs false 禁止 file 域跨域访问
setMixedContentMode NEVER_ALLOW 禁止 HTTPS 页面加载 HTTP 资源
setWebContentsDebuggingEnabled false 发布包必须关闭调试
addJavascriptInterface 谨慎使用 必须加 @JavascriptInterface 注解

另外,WebView 的 User-Agent 不要暴露太多信息。默认的 UA 里包含系统版本、设备型号,这些信息可能被用来做指纹追踪。我一般会重写 UA,只保留必要的标识。

Map<String, String> headers = new HashMap<>();
headers.put("User-Agent", "MyApp/1.0");
webView.loadUrl(url, headers);

知识体系总览

下面这张图,把 WebView 安全的四个维度串起来了。你可以把它当作一个检查清单,每次接入 WebView 时对照着看一遍。

WebView 安全四维防护 ① JavaScript 接口漏洞 addJavascriptInterface 反射攻击 ② 远程代码执行 Chrome 内核漏洞 / javascript: 协议注入 ③ 文件访问风险 file:// 协议读取本地敏感文件 ④ 安全配置最佳实践 7 项核心配置 + 运行时校验 核心原则 最小权限原则 · 白名单校验 · 输入输出双重过滤 关闭不需要的功能 · 及时更新 WebView 内核

最后说一句,WebView 安全没有银弹。你配置得再严,如果加载的页面本身有 XSS 漏洞,攻击者还是能绕过一些限制。所以,前后端一起做安全,才是正解


公众号:蓝海资料掘金营,微信deep3321