Appearance
WebView H5 JSBridge 交互与安全防护
回到总览:混合开发与桥接
相关模块:PlatformChannel 深度解析:MethodChannel / EventChannel / BasicMessageChannel · H5 登录态与 Cookie 强注入 SSO 机制
一句话定义
JSBridge 是 H5 混合开发(Hybrid)中连接 Web 前端 JavaScript 与 Native(Android / iOS)原生的桥梁协议;通过注入对象或 URL Scheme 拦截,实现 H5 调用 Native 原生能力(如相机、支付、位置)与 Native 向 H5 异步回调的双向通信。
代码索引
为什么需要
- 为什么 Android 4.2 (API 17) 之前使用
addJavascriptInterface会引发严重的安全 RCE 漏洞?- 一句话答:在 API 17 之前,被注入的 Java 对象的所有公共方法(包括反射方法
getClass().forName())均可被 JS 任意调用;攻击者可在网页中植入恶意 JS,通过反射执行任意 Linux 系统 Shell 命令。
- 一句话答:在 API 17 之前,被注入的 Java 对象的所有公共方法(包括反射方法
- 现代 JSBridge 的三大主流通信方式是什么?
- 一句话答:1. 注入 API 对象 (
@JavascriptInterface/wkWebView.addScriptMessageHandler);2. URL Scheme 拦截 (onJsPrompt/shouldOverrideUrlLoading);3. 自建 MessageQueue 消息队列。
- 一句话答:1. 注入 API 对象 (
- 如何防止任意恶意第三方 H5 页面私自调用敏感的 Native JSBridge(如支付、获取 Auth Token)?
- 一句话答:必须建立白名单域名校验机制 (White-list Domain Verification);在 JSBridge 触发时校验当前 WebView 的
url,只放行官方安全域名的调用。
- 一句话答:必须建立白名单域名校验机制 (White-list Domain Verification);在 JSBridge 触发时校验当前 WebView 的
底层机制
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 WebView | iOS WKWebView | Flutter webview_flutter |
|---|---|---|---|
| JS 注入 API | addJavascriptInterface + @JavascriptInterface | WKScriptMessageHandler | JavaScriptChannel |
| ** 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-demo | JSBridge 双向通信 + 敏感接口域名白名单拦截 | WebViewJsBridgeActivity.kt · jsbridge_demo.html |
复习检查题
在 Android 4.2 之前,
addJavascriptInterface为什么会导致严重的安全 RCE(远程代码执行)漏洞?答:在 API 17 之前,被注入的 Java 对象中的所有公共方法都可以被网页里的 JS 直接调用,攻击者可以通过 JS 拿到 Java 的
getClass(),再利用反射调用java.lang.Runtime.getRuntime().exec()在 Android 系统上执行任意 Linux Shell 命令,从而控制设备。API 17 之后强制要求被调用的 Java 方法必须标注@JavascriptInterface注解。为什么 Native 回调 JS 推荐使用
evaluateJavascript()而不是老的loadUrl("javascript:...")?答:因为
loadUrl("javascript:...")会导致整个 WebView 重新解析 URL,效率极低且会刷新页面;而evaluateJavascript()是 Android 4.4 引入的专门用于执行 JS 的高效 API,它不会触发页面刷新,支持获取 JS 执行结果的返回值回调,且执行效率高得多。
速记
- 安全第一:注入方法必加
@JavascriptInterface,敏感 Bridge 必验域名白名单。 - 回调推荐:Native 调 JS 统一使用
evaluateJavascript,高效且支持返回值。 - 线程合规:evaluateJavascript 必须在 UI 主线程执行。