WebView 以宿主 App 的全部权限运行 Web 内容,而 JavaScript 桥是一处没有围墙的提权面。Android 上 addJavascriptInterface 把 Java 对象注入每个帧(含 iframe),却无法核验调用方的来源;Android 4.2 之前它是赤裸裸的远程代码执行,今天「宽泛的桥 + 被反射的不可信内容」仍是能以宿主权限跑原生代码的 XSS。web.dev 确认 WebView 没有真正的站点隔离,并把 Cookie 与 DOM 双手奉给宿主。2026 年 CVE-2026-103922(Capacitor,CVSS 9.6)在已上线的混合 App 里演示了这个同源边界的失守。宿主自有 runtime 把墙画出来:最小权限、按来源作用域、按能力设闸。

桥信任每一个调用方

原生桥是 Web 与原生之间的那扇门,而默认情况下它是没锁的。Android 的 addJavascriptInterface 把一个 Java 对象暴露给 WebView 的每一个帧,包括任何 iframe,平台自己也承认「没有机制核验调用帧的来源」。含义很直接:往一个合法页面里注入一个恶意帧,那个帧就能调用你的原生方法。以 API 16 及更早为目标的应用,JavaScript 可以完全控制宿主 App;即便今天,把不可信的用户内容反射进一个启用桥的 WebView,就是一条以宿主权限执行 Java 的 XSS 路径——这是提权,不是小毛病。基于 MessageChannel 的桥只有你自己加上来源校验才更安全;不加,任何发送者发来的消息都会被接受。

没有站点隔离,就没有真正的墙

一个经过加固的浏览器会把不可信页面分隔进互相隔离的进程,让一个标签页读不到另一个的 Cookie 或内存。WebView 不是这样。据 web.dev,在 Android 8.0 之前渲染进程与宿主 App 同进程运行,而即便在更新的系统上,所有页面与 WebView 实例仍共享单一进程——所以真正的站点隔离不可能实现。这正是让数十亿设备暴露在 Meltdown 与 Spectre 之下的原因:一个恶意页面可以尝试读取它本不该看到的跨源数据。WebView 还把渲染内容的完全访问权(包括 Cookie 和 DOM)交给宿主 App,且缺少真实浏览器提供的安全 UI 指示器——用户看到的地址栏可能与实际显示的页面不一致,甚至发生在 HTTPS 连接上,而且没有 API 能发现它。

2026 年的真实失守

这不是理论。2026 年,主流混合框架 Capacitor 爆出 CVE-2026-103922,CVSS 9.6。它的 WebView 导航守卫校验了目标 URL 的 scheme 和 host,却没校验路径,于是构造的链接可以命中一个以应用自身来源托管的内部代理端点。原生层随后拉取攻击者控制的内容,并以应用的受信来源返回,让恶意 JavaScript 以同源权限运行——读取 localStorage、Cookie,以及已注册插件暴露的任何原生能力。它对应 CWE-346(来源校验错误)与 CWE-441(混乱 Deputy)。即便禁用 HTTP 插件也无济于事,因为那个处理器始终在线。一条普通的链接,在 App 内渲染,就成了带着 App 全部信任的脚本执行通道。

runtime 解法

宿主自有 runtime 默认把 Web 当作不可信对象,并画出一条显式的安全边界。桥按来源作用域、最小权限:一个来自不可信 catalog 条目的页面够不到凭据或设备处理器,它只能拿到宿主显式授予的能力。Web 上下文按条目沙箱化,导航逐帧校验,原生层检查而非信任每一次调用。墙是写进策略里的,而不是留给一个「谁来敲都信」的默认。

维度WebView宿主自有 runtime
桥暴露面Java 对象注入每帧,无来源校验按来源作用域、最小权限、按能力设闸
进程隔离单一共享进程,无站点隔离按 catalog 条目沙箱化 Web 上下文
跨源窃取Meltdown/Spectre 级别暴露原生层逐项校验每次调用
安全指示器无;地址栏可能与页面不符宿主可控、可核验的信任
同源边界默认受信(CVE-2026-103922)显式、按来源强制

本系列始终绕不开一个词:控制权。WebView 信任它加载的 Web,并把 App 的权限双手奉上;边界静默失守,而 2026 年告诉我们,它会以灾难性方式失守。runtime 把边界变成一堵由你修筑的墙——按来源、按能力、按页面。对一套承载第三方页面在你的 App 里交易的 catalog 来说,这堵墙,就是「平台」与「负债」的分界线。

参考来源