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世界的桥梁。桥修好了,数据流通顺畅;桥没修好,什么妖魔鬼怪都能进来。

WebView 安全 JavaScript Bridge 接口 addJavascriptInterface @JavascriptInterface 注解 shouldOverrideUrlLoading XSS 漏洞 反射型 XSS 存储型 XSS DOM 型 XSS 远程代码执行 CVE-2012-6636 CVE-2014-1939 防护策略:最小化接口 + 输入校验 + 版本升级

一、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时,把用户输入直接插入到页面中。比如innerHTMLdocument.write这些操作。

我曾经审计过一个社交App,它的聊天功能用WebView渲染消息内容。用户发送的消息直接通过loadDataWithBaseURL加载到WebView里。攻击者发了一条包含<img src=x onerror="alert(1)">的消息,所有看到这条消息的用户都弹窗了。更可怕的是,如果攻击者把alert(1)换成调用Bridge接口获取通讯录的代码……你想想看后果。

XSS防护三板斧

  1. 输出编码:所有用户输入的内容,在输出到HTML之前必须做HTML实体编码。<变成&lt;>变成&gt;
  2. Content Security Policy (CSP):通过HTTP头或meta标签限制页面只能加载特定来源的脚本。比如script-src 'self',只允许加载同源脚本。
  3. 禁用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_accessibilityTraversalBeforeaccessibilityTraversalAfter等内置接口。
  • 使用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