Skip to content

WebView H5 JSBridge 交互与安全防护 ​

回到总览:混合开发与桥接
相关模块:PlatformChannel 深度解析:MethodChannel / EventChannel / BasicMessageChannel · H5 登录态与 Cookie 强注入 SSO 机制

一句话定义 ​

JSBridge 是 H5 混合开发(Hybrid)中连接 Web 前端 JavaScript 与 Native(Android / iOS)原生的桥梁协议;通过注入对象或 URL Scheme 拦截,实现 H5 调用 Native 原生能力(如相机、支付、位置)与 Native 向 H5 异步回调的双向通信。

代码索引 ​

对应 Lab:webview-jsbridge-demo · WebViewJsBridgeActivity.kt

为什么需要 ​

  • 为什么 Android 4.2 (API 17) 之前使用 addJavascriptInterface 会引发严重的安全 RCE 漏洞?
    • 一句话答:在 API 17 之前,被注入的 Java 对象的所有公共方法(包括反射方法 getClass().forName())均可被 JS 任意调用;攻击者可在网页中植入恶意 JS,通过反射执行任意 Linux 系统 Shell 命令。
  • 现代 JSBridge 的三大主流通信方式是什么?
    • 一句话答:1. 注入 API 对象 (@JavascriptInterface / wkWebView.addScriptMessageHandler);2. URL Scheme 拦截 (onJsPrompt / shouldOverrideUrlLoading);3. 自建 MessageQueue 消息队列。
  • 如何防止任意恶意第三方 H5 页面私自调用敏感的 Native JSBridge(如支付、获取 Auth Token)?
    • 一句话答:必须建立白名单域名校验机制 (White-list Domain Verification);在 JSBridge 触发时校验当前 WebView 的 url,只放行官方安全域名的调用。

底层机制 ​

1. JSBridge 注入对象与回调闭环 ​

调用详情:JS 通过 window.NativeBridge.call(action, callbackId) 发起请求;Native 通过 evaluateJavascript("window.NativeBridge.onCallback(callbackId, data)") 异步回调;每次调用必须先校验白名单域名再执行业务逻辑。

2. URL Scheme 拦截模式 (prompt / iframe) ​

在无法直接注入对象或旧版本系统上,通过拦截 prompt() 或 iframe.src 变动实现:

Scheme 形式:prompt('jsbridge://call?data=...') —— Android 通过 WebChromeClient.onJsPrompt 拦截,iOS 通过 shouldOverrideUrlLoading 拦截。适用于不支持直接注入对象的旧系统。

Android / Flutter / Web / Backend 对照 ​

维度Android WebViewiOS WKWebViewFlutter webview_flutter
JS 注入 APIaddJavascriptInterface + @JavascriptInterfaceWKScriptMessageHandlerJavaScriptChannel
** Native 执行 JS**evaluateJavascript("jsCode", callback)evaluateJavaScript:completionHandler:runJavaScriptReturningResult()
RCE 漏洞防护必须加 @JavascriptInterface 注解 (API 17+)天然隔离防反射继承 Native 安全机制

常见场景 ​

1. 简易双向 JSBridge 注入实现 (Android) ​

java
public class JSBridge {
    private WebView mWebView;

    @JavascriptInterface
    public void callNative(String action, String jsonArgs, final String callbackId) {
        // 白名单安全校验
        String currentUrl = mWebView.getUrl();
        if (!isSafeDomain(currentUrl)) {
            Log.e("JSBridge", "非白名单域名拒绝调用: " + currentUrl);
            return;
        }

        if ("getLocation".equals(action)) {
            String locationJson = "{\"lat\": 39.9, \"lng\": 116.4}";
            // 切回主线程回调 JS
            mWebView.post(() -> {
                String js = String.format("window.JSBridge.onCallback('%s', %s);", callbackId, locationJson);
                mWebView.evaluateJavascript(js, null);
            });
        }
    }
}

常见误配、事故后果与排障 ​

1. 事故:未做域名白名单校验导致用户凭证在任意网页被盗取 ​

  • 误配原因:WebView 加载了第三方广告或用户自定义 H5 链接,且 Native 暴露了 getAuthToken() JSBridge 接口却未校验域名。
  • 后果:第三方网页直接运行 window.NativeBridge.getAuthToken(),轻松盗取用户的登录 Token 并发送给恶意服务器!
  • 排障与修法:在所有敏感 JSBridge 执行前,显式提取 Uri.parse(webView.getUrl()).getHost() 并与安全白名单库匹配。

对应实验 ​

Lab说明源码
webview-jsbridge-demoJSBridge 双向通信 + 敏感接口域名白名单拦截WebViewJsBridgeActivity.kt · jsbridge_demo.html

复习检查题 ​

  1. 在 Android 4.2 之前,addJavascriptInterface 为什么会导致严重的安全 RCE(远程代码执行)漏洞?

    答:在 API 17 之前,被注入的 Java 对象中的所有公共方法都可以被网页里的 JS 直接调用,攻击者可以通过 JS 拿到 Java 的 getClass(),再利用反射调用 java.lang.Runtime.getRuntime().exec() 在 Android 系统上执行任意 Linux Shell 命令,从而控制设备。API 17 之后强制要求被调用的 Java 方法必须标注 @JavascriptInterface 注解。

  2. 为什么 Native 回调 JS 推荐使用 evaluateJavascript() 而不是老的 loadUrl("javascript:...")?

    答:因为 loadUrl("javascript:...") 会导致整个 WebView 重新解析 URL,效率极低且会刷新页面;而 evaluateJavascript() 是 Android 4.4 引入的专门用于执行 JS 的高效 API,它不会触发页面刷新,支持获取 JS 执行结果的返回值回调,且执行效率高得多。

速记 ​

  • 安全第一:注入方法必加 @JavascriptInterface,敏感 Bridge 必验域名白名单。
  • 回调推荐:Native 调 JS 统一使用 evaluateJavascript,高效且支持返回值。
  • 线程合规:evaluateJavascript 必须在 UI 主线程执行。

站点构建时间:2026/8/24 23:43:17