如何通过 addJavascriptInterface 实现 JS 调用 Android 方法?存在哪些安全漏洞?

解读

国内面试中,这道题既考察“JSBridge”基础实现,也暗含对混合开发安全红线的认知。面试官通常先让你口述最小可运行流程,再追问“低版本系统漏洞”“意图劫持”“代码注入”等场景,最终落到“如何闭环加固”。回答时要体现:

  1. 最小实现 → 2. 漏洞原理 → 3. 国内合规要求(工信部 164 号文、个人信息保护合规)→ 4. 线上加固方案。
    切忌只背“@JavascriptInterface”关键字,而要展示“攻击—检测—加固—回滚”全链路视角。

知识点

  1. WebView.addJavascriptInterface(Object object, String jsName) 机制
  2. Java 反射与注解 @JavascriptInterface(API 17+ 强制)
  3. 漏洞根源:
    • 2.x-4.1 系统 searchBoxJavaBridge_、accessibility 等内置接口未移除
    • 任意 file:// 协议可跨域,mWebView.loadUrl("javascript:...") 注入
    • 意图劫持:js 调用 openApp 后通过 Intent.parseUri 触发 exported 组件
    • 信息泄漏:getDeviceId、getInstalledPackages 等敏感接口未做运行时审批
  4. 国内合规:
    • 工信部 164 号文要求 WebView 远程调试端口必须默认关闭
    • 《个人信息保护法》要求敏感调用需“双清单”+“弹窗同步”
  5. 加固手段:
    • 移除内置接口、setAllowFileAccess(false)、setAllowFileAccessFromFileURLs(false)
    • 自定义白名单注解 + 运行时权限检查
    • 对 js 入参做 JSON Schema 校验,禁止反射链(getClass、forName、exec)
    • 采用 WebViewAssetLoader 或 https 本地缓存,避免 file://
    • 线上热修复:WebView 独立进程 + 热更新 SDK(如 Tinker、Sophix)
    • 异常日志回捞:结合 Matrix、Sliver 捕获 Java 层与 JS 层堆栈

答案

【最小实现】

  1. 启用 JavaScript:webView.getSettings().setJavaScriptEnabled(true);
  2. 定义桥类:
    class AndroidBridge {
    @JavascriptInterface
    public String getToken(String scope) {
    // 国内合规:运行时再次检查权限
    if (!PrivacyHelper.isUserAuthorized(scope)) {
    return "";
    }
    return TokenManager.getScopedToken(scope);
    }
    }
  3. 绑定接口:webView.addJavascriptInterface(new AndroidBridge(), "native");
  4. 前端调用: <script> const token = window.native.getToken('profile'); </script>

【漏洞清单与修复】

  1. 系统级接口残留:在 onDestroy 中调用 webView.removeJavascriptInterface("searchBoxJavaBridge_") 等;
  2. file:// 跨域:统一用 WebViewAssetLoader 映射 https://appassets.androidplatform.net;
  3. 意图劫持:对 openApp 方法内部使用 Intent.parseUri 前先 setComponent(null) 并校验包名签名;
  4. 敏感数据泄露:getToken 内部接入移动智能终端补充设备标识(OAID)体系,禁止直接返回 IMEI;
  5. 代码注入:对入参做正则过滤,禁止包含 “getClass”、“Runtime”、“exec” 关键字;
  6. 远程调试:发布包必须 WebView.setWebContentsDebuggingEnabled(false),通过 BuildConfig.DEBUG 动态开关;
  7. 回滚策略:若线上发现接口被脱库,通过热更新 SDK 下发空实现,同时上报风控。

拓展思考

  1. 替代方案:
    • 使用 @JavascriptInterface 仅做“信令通道”,业务数据走 WebMessagePort 或 OkHttp 拦截器,实现“JS 只传 ID,Android 回抛结果”的零信任模型;
    • 对性能敏感场景(小程序容器)可改用 V8 直接绑定,绕过 WebView 的 Binder 开销。
  2. 纵深防御:
    • 在 Native 层对 libwebviewchromium.so 做二次打包,插入 Inline Hook 检测可疑 JNI 调用;
    • 结合 RASP(Runtime Application Self-Protection)对 Java 反射链进行实时熔断。
  3. 合规趋势:
    • 工信部正在征求意见的《移动智能终端应用软件 SDK 安全规范》拟要求“JSBridge 调用必须在隐私政策中逐条列出”,后续需动态生成桥方法列表供用户撤销。