混合开发已经成为移动端的主流方案之一,WebView承担着展示H5页面并桥接原生能力的职责。然而一旦JS与原生的交互通道被滥用,攻击者就可能通过一个恶意网页反向控制App的Java层代码,造成用户数据泄露甚至远程执行任意命令。本文从WebView常见的几类安全漏洞入手,结合实际案例与代码,逐一说明JS交互时需要特别注意的事项。

一、addJavascriptInterface远程代码执行漏洞
这是WebView历史上最严重的漏洞之一。在Android 4.2之前,通过addJavascriptInterface注入到WebView的对象,其所有public方法都会暴露给JS,包括那些继承自Object类的反射相关方法。攻击者只需让WebView加载一个恶意页面,就能利用getClass配合反射机制执行任意系统命令。
典型的攻击代码非常简短,这也是该漏洞危害巨大的原因:
function execute(cmd) {
// 利用addJavascriptInterface注入的对象进行反射调用
return jsObj.getClass().forName('java.lang.Runtime')
.getMethod('getRuntime', null)
.invoke(null, null)
.exec(cmd);
}
防护要点有三条。第一,Android 4.2及以上的系统要求被JS调用的方法必须显式标注@JavascriptInterface注解,未标注的方法不会暴露给JS,务必确认所有暴露方法都添加了该注解:
public class SafeBridge {
// 只有标注了注解的方法才会暴露给JS
@JavascriptInterface
public String getUserToken() {
return TokenManager.getScopedToken();
}
// 这个方法没有注解,JS无法访问
public String getRawPassword() {
return PasswordStore.read();
}
}
第二,不要将包含敏感操作的通用对象直接注入。注入对象应做最小化设计,只暴露确实需要的只读方法。第三,对于必须支持低版本系统的场景,服务端应禁止WebView加载不可信的第三方URL,并对所有跳转链接做白名单校验。
二、File协议与域限制带来的本地文件泄露
WebView默认开启文件访问能力时,本地HTML文件可以通过File协议读取应用私有目录中的内容。如果攻击者诱导App加载了SD卡上一个恶意HTML,该页面就能遍历/data/data/包名/下的私有文件,把数据库、SharedPreferences里的登录凭证、聊天记录全部发送出去。
Android 4.1之后系统默认关闭了File域下的跨域访问,但很多开发者为了兼容旧页面会手动重新打开,这是非常危险的做法。正确的配置应该是:
WebSettings settings = webView.getSettings(); // 禁止通过file协议访问本地文件 settings.setAllowFileAccess(false); // 禁止file域页面通过XHR读取其他本地文件,默认应为false settings.setAllowFileAccessFromFileURLs(false); // 禁止file域页面读取http/https资源 settings.setAllowUniversalAccessFromFileURLs(false);
需要注意setAllowFileAccessFromFileURLs与setAllowUniversalAccessFromFileURLs默认值即为false,切勿为了解决某个本地页面加载问题而贸然改为true。如果业务确实需要加载本地页面,建议将HTML资源放入assets目录并通过https:或自定义协议代理访问,同时在shouldOverrideUrlLoading中对URL做域名白名单判断:
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
String host = request.getUrl().getHost();
if (!TRUSTED_HOSTS.contains(host)) {
// 非白名单域名直接拦截,跳转到外部浏览器
openInSystemBrowser(request.getUrl());
return true;
}
return false;
}
此外,还要警惕通过Intent传入的URL直接交给WebView加载的场景。二维码扫描、Push消息中的跳转链接都属于外部输入,必须经过校验后才能进入WebView,否则用户可能被引导到钓鱼页面并触发上述File协议攻击。
三、JS交互通道的安全设计与证书校验
即使堵住了前两个漏洞,JS桥本身的设计缺陷依然可能成为突破口。一个常见错误是桥接方法缺少权限分层:例如提供pay(orderInfo)这样的方法,任何页面都能调用,包括被嵌入的第三方广告页。正确的做法是为每个桥接方法声明所需的权限等级,在调用时校验当前页面的域名是否具备该权限。
同时建议对通信内容做签名验证。原生侧在注入桥对象前,先向页面下发一个一次性token,页面每次调用桥方法时携带该token,原生侧校验通过后才执行业务逻辑。这样即使恶意页面成功注入脚本,也无法伪造合法调用。
证书校验是另一块容易被忽视的短板。为了抓包调试,一些开发者重写了onReceivedSslError并直接调用handler.proceed(),这个配置如果带入线上版本,等于放弃了HTTPS的防中间人能力,攻击者在公共WiFi下即可窃听全部通信内容。正确做法是只在测试构建中放行证书错误:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if (BuildConfig.DEBUG && ALLOW_DEBUG_SSL) {
handler.proceed(); // 仅调试环境放行
} else {
handler.cancel(); // 线上环境一律取消
}
}
更进一步,可以重写shouldInterceptRequest对响应内容做关键接口的证书锁定(Certificate Pinning),或者使用OkHttp拦截替换关键请求,确保登录、支付等敏感流量不经过可被篡改的通道。
最后几个容易遗漏的点:一是setSavePassword早已废弃且默认关闭,切勿手动开启明文密码保存;二是关闭setJavaScriptEnabled不需要JS的页面,缩小攻击面;三是通过setWebContentsDebuggingEnabled开启的远程调试只能在Debug构建中启用,否则任何人用Chrome的inspect面板都能查看你的WebView内容;四是onJsAlert、onJsPrompt等回调中返回的Prompt桥接方案,要防止页面伪造弹窗骗取用户授权。安全从来不是单点配置,而是从URL入口、桥接权限、传输层到调试开关的一整条链路的层层设防。
WebView安全JS交互JavaScript注入防护修改时间:2026-09-01 02:44:30