WebView安全:JavaScript Bridge接口、XSS漏洞、WebView远程代码执行
WebView,说白了就是App里内嵌的一个浏览器。很多App用它来展示网页内容、加载H5页面,甚至承载核心业务逻辑。但问题来了——WebView一旦配置不当,就成了攻击者进入App内部的后门。
我这些年做逆向分析,见过太多因为WebView翻车的案例。有的App直接把整个业务逻辑丢给WebView,结果被XSS一锅端;有的JavaScript Bridge接口没做鉴权,被第三方页面随意调用;更夸张的,WebView远程代码执行漏洞一出,手机直接变肉鸡。
今天咱们就把WebView安全这块掰开揉碎,讲清楚三个核心问题:JavaScript Bridge接口怎么保护?XSS漏洞怎么防?远程代码执行到底是怎么回事?
核心认知:WebView不是简单的浏览器控件,它是App与Web世界的桥梁。桥修好了,数据流通顺畅;桥没修好,什么妖魔鬼怪都能进来。
一、JavaScript Bridge接口:App与Web的通信管道
JavaScript Bridge,也叫JS Bridge,是App原生代码和WebView中JavaScript代码互相调用的通道。Android上最常用的方式就是addJavascriptInterface方法。
举个例子,App里有个WebView加载了H5页面,H5页面想调用手机摄像头拍照。这时候就需要JS Bridge——H5通过JS调用原生接口,原生代码执行拍照逻辑,再把结果返回给H5。
听起来很美好对吧?但问题来了——这个接口是双向的。JS能调用原生,原生也能调用JS。如果JS那边被注入了恶意代码,它就能通过Bridge调用原生方法,甚至执行系统命令。
⚠️ 高危操作:Android 4.2以下版本,addJavascriptInterface注册的所有方法默认对JS可见。这意味着任何加载到WebView的页面,包括第三方广告、恶意链接,都能直接调用这些方法。
我记得有个金融类App,为了快速迭代,把登录、支付、用户信息查询全部通过JS Bridge暴露给H5。结果H5页面被XSS攻击,攻击者直接调用了getUserInfo()方法,把用户的身份证号、银行卡号全拿走了。嗯,这个案例后来成了我们内部培训的反面教材。
安全配置要点
- 使用@JavascriptInterface注解:Android 4.2+要求必须显式注解,未注解的方法JS无法调用。这是最低门槛。
- 最小化暴露原则:只暴露必要的方法,不要图省事把整个类传进去。我见过有人直接把
Context传进去的,那等于把家门钥匙给了陌生人。 - URL白名单校验:在
shouldOverrideUrlLoading中校验来源URL,只允许可信域名调用Bridge接口。 - 参数校验:所有从JS传入的参数,必须做类型检查和范围校验。别信JS传过来的任何数据。
// 安全示例:使用@JavascriptInterface注解
public class JSBridge {
private Context mContext;
public JSBridge(Context context) {
this.mContext = context;
}
@JavascriptInterface
public String getUserToken() {
// 校验调用来源
if (!isTrustedOrigin()) {
return "";
}
// 返回加密后的token
return encryptToken(getTokenFromSafeStorage());
}
@JavascriptInterface
public void shareContent(String title, String url) {
// 参数校验
if (title == null || title.length() > 100) {
return;
}
if (url == null || !url.startsWith("https://")) {
return;
}
// 执行分享逻辑
doShare(title, url);
}
private boolean isTrustedOrigin() {
// 获取当前页面URL并校验白名单
return true; // 实际项目中需要实现
}
}
💡 个人习惯:我一般会在JS Bridge的每个方法入口加一个调用栈检查,确认调用来自WebView的主线程,而不是被其他方式注入的。虽然不能完全防住,但能挡住大部分低级攻击。
二、XSS漏洞:WebView的隐形杀手
XSS(跨站脚本攻击)在WebView中比在浏览器中更危险。为什么?因为WebView里的JS能通过Bridge调用原生接口,攻击面更大。
XSS分三种:反射型、存储型、DOM型。在移动App里,最常见的是反射型和DOM型。
反射型XSS:用户输入的内容直接拼接到HTML中返回。比如搜索框输入<script>alert('xss')</script>,如果App没做转义,这段脚本就会执行。
存储型XSS:恶意脚本被存到服务器,每次加载页面都执行。比如评论区、用户头像URL这些地方。
DOM型XSS:前端JS代码动态修改DOM时,把用户输入直接插入到页面中。比如innerHTML、document.write这些操作。
我曾经审计过一个社交App,它的聊天功能用WebView渲染消息内容。用户发送的消息直接通过loadDataWithBaseURL加载到WebView里。攻击者发了一条包含<img src=x onerror="alert(1)">的消息,所有看到这条消息的用户都弹窗了。更可怕的是,如果攻击者把alert(1)换成调用Bridge接口获取通讯录的代码……你想想看后果。
XSS防护三板斧
- 输出编码:所有用户输入的内容,在输出到HTML之前必须做HTML实体编码。
<变成<,>变成>。 - Content Security Policy (CSP):通过HTTP头或meta标签限制页面只能加载特定来源的脚本。比如
script-src 'self',只允许加载同源脚本。 - 禁用JavaScript(非必要场景):如果WebView只是展示静态内容,直接
webView.getSettings().setJavaScriptEnabled(false)。简单粗暴,但有效。
// 安全加载HTML内容
String safeHtml = sanitizeHtml(userInput); // 使用OWASP Java HTML Sanitizer
webView.loadDataWithBaseURL(null, safeHtml, "text/html", "UTF-8", null);
// 设置CSP
Map<String, String> headers = new HashMap<>();
headers.put("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");
webView.loadUrl(url, headers);
⚠️ 避坑指南:我曾经遇到一个团队,他们用WebViewClient.shouldInterceptRequest来拦截所有请求,想通过这种方式过滤XSS。结果发现性能开销巨大,而且很多XSS payload根本不在请求里,而是在页面DOM操作中。这条路走不通。
三、WebView远程代码执行:最致命的漏洞
远程代码执行(RCE)是WebView安全里最严重的问题。攻击者通过WebView漏洞,直接在App进程中执行任意代码。这意味着什么?意味着攻击者能拿到App的所有权限,读取文件、发送短信、调用摄像头,甚至提权到系统级别。
历史上最著名的两个WebView RCE漏洞:
| 漏洞编号 | 影响版本 | 攻击方式 | 危害等级 |
|---|---|---|---|
| CVE-2012-6636 | Android 4.1及以下 | 通过addJavascriptInterface反射调用Runtime.exec | 严重 |
| CVE-2014-1939 | Android 4.4以下 | 通过WebView的searchBoxJavaBridge_接口 | 严重 |
CVE-2012-6636的原理很简单:在Android 4.1及以下版本,addJavascriptInterface注册的对象,JS可以通过反射调用它的任何方法。攻击者构造一个页面,JS代码遍历对象的所有方法,找到Runtime.getRuntime(),然后调用exec()执行系统命令。
我记得当时有个漏洞利用代码,就几行JS:
// 恶意JS代码示例(仅用于理解漏洞原理)
function executeCommand(cmd) {
// 遍历JS Bridge对象的所有方法
for (var method in window) {
try {
if (method.contains("getClass")) {
// 通过反射获取Runtime对象
var runtime = window[method]().forName("java.lang.Runtime");
var execMethod = runtime.getMethod("exec", [String.class]);
execMethod.invoke(runtime, [cmd]);
}
} catch(e) {}
}
}
executeCommand("id"); // 执行系统命令
看到没?就这么几行代码,攻击者就能在用户手机上执行任意命令。我当时分析过一个恶意样本,它利用这个漏洞下载了远控木马,然后通过Runtime.exec执行chmod 777提权,最后把手机变成了肉鸡。
RCE防护策略
- 升级WebView内核:Android 4.4+使用Chromium内核,修复了大部分RCE漏洞。如果App支持,尽量使用系统WebView或独立WebView内核。
- 移除危险接口:Android 4.4以下版本,手动移除
searchBoxJavaBridge_、accessibilityTraversalBefore、accessibilityTraversalAfter等内置接口。 - 使用Safe Browsing:Android 8.0+支持Google Safe Browsing,可以拦截已知恶意页面。
- 最小化WebView版本:如果业务不需要JS,直接禁用。如果需要JS,只加载可信内容。
// 移除内置危险接口(Android 4.4以下)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
webView.removeJavascriptInterface("searchBoxJavaBridge_");
webView.removeJavascriptInterface("accessibilityTraversalBefore");
webView.removeJavascriptInterface("accessibilityTraversalAfter");
}
// 启用Safe Browsing(Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WebView.startSafeBrowsing(context, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
Log.d("SafeBrowsing", "启动结果: " + success);
}
});
}
核心原则:WebView安全没有银弹。JS Bridge、XSS、RCE这三个问题往往相互关联——XSS可能触发JS Bridge调用,JS Bridge配置不当可能导致RCE。所以防护必须是体系化的,从接口设计、输入校验、版本管理三个维度同时下手。
最后说一句,WebView安全这块,我建议每个移动开发者和安全工程师都亲手复现一遍经典漏洞。只有自己踩过坑,才知道怎么填坑。别问我为什么知道——我当年在测试环境里用CVE-2012-6636把自己手机搞重启了三次,才真正理解了这个漏洞的威力。
公众号:蓝海资料掘金营,微信deep3321