系统剪贴板是一条全局、跨 App 的通道,而 WebView 页面能读走用户复制的一切——银行卡、密码、一次性验证码。Android 上剪贴板在 Android 10 之前可被任何后台 App 静默读取,直到 Android 12 才加上访问 Toast 与敏感数据标记。iOS 的通用粘贴板是系统级、跨重启持久、还能经 Universal Clipboard 跨设备同步;iOS 14 加了读取通知、iOS 16 加了粘贴确认。但这些原生缓解都碰不到 WebView 内那条 Web 读取路径:一个有权限、带用户手势的页面,就能收割 App 刚为用户复制的凭据。宿主自有 runtime 在原生层中介剪贴板——把敏感项标为 localOnly 并设过期、把 Web 读取关在显式用户意图之后。

剪贴板曾是全局窃听

Android 的剪贴板是一套在应用之间复制粘贴数据的框架,实现不当就会把用户数据暴露给其他 App(Android 开发者,OWASP MASVS-CODE)。暴露程度随版本不同:Android 10 之前,后台 App 能静默读取前台 App 的剪贴板——任何复制的密码、验证码或卡号都对恶意 App 敞开。从 Android 12 起,系统每次有 App 访问剪贴板都会弹出一个 Toast,开发者还能用 EXTRA_IS_SENSITIVE 标记敏感数据,在键盘 UI 里把预览遮糊。这帮了原生「写入」的忙,却对 Web 页面「读取」毫无作用。

iOS 让它持久且跨设备同步

在 iOS 上,通用粘贴板在所有前台 App 间共享,自 iOS 10 起还会通过 Universal Clipboard 同步到附近 Apple 设备;它跨重启、跨重装持久存在,除非被清除(OWASP MASTG-KNOW-0083)。自 iOS 14 起,当 App 读取由另一个 App 写入的粘贴板内容时系统会通知用户;自 iOS 16 起会弹出粘贴确认。具名粘贴板按 App 或团队作用域隔离,但大多数 App 直接用通用的那块。而那个权限弹窗本身也是一笔税:电商 App 在启动时读剪贴板以识别复制的商品链接,如今会拿粘贴确认弹窗狂轰用户——而且 detectPatterns 仍能在不返回值的情况下泄露「剪贴板内容像一个 URL、号码或电话」。

在 WebView 里,Web 也会读它

这才是原生缓解漏掉的那一环。WebView 页面可以借一个用户手势调用 Clipboard API 或 execCommand('paste'),读走用户复制的一切。再叠加混合 App 里早已普遍的自动填充与验证码自动读取模式,一个恶意或被攻陷的 catalog 页面就成了凭据收割机:用户复制一次性验证码准备登录,页面读走它,会话随之丢失。原生的 EXTRA_IS_SENSITIVE 标记保护的是你的 App「写」出去的东西,而 Web 页面的「读」遵循的是同一套浏览器剪贴板规则——WebView 并不会像原生页面那样执行宿主的意图。剪贴板本为便利而生,却变成了一扇平台自身隐私控制也关不上的后门。

runtime 解法

宿主自有 runtime 在原生层中介剪贴板访问,而不是把它留给页面。敏感项以 localOnly 加 expirationDate 写出,使其永不进入 Universal Clipboard、也活不过一次重启。Web 剪贴板读取被关在显式用户意图之后——一个可见的粘贴控件,而非静默读取——不可信的 catalog 条目则被彻底拒绝剪贴板访问。便利留着,后门关上。

维度WebView宿主自有 runtime
剪贴板作用域全局、跨 App,iOS 上同步且持久原生层中介,localOnly + 过期
后台读取Android 10 前静默;12 起有 Toast宿主把关每一次 Web 读取
2FA / 验证码暴露复制的码任何页面可读不可信条目拒绝;按意图放行
Web 读取路径带手势即 Clipboard API,无管控可见粘贴控件,无静默读
原生缓解缺口EXTRA_IS_SENSITIVE 只护写入写入与 Web 读取双双覆盖

本系列反复回到同一课:WebView 继承了平台最弱的那些面,还额外加了一个「Web 形状」的面。剪贴板是一种便利,在 WebView 里却变成一处操作系统都无法彻底关上的跨 App、跨页面窃听。runtime 把宿主安插在页面与剪贴板之间——于是便利是 App 的,暴露却不是。

参考来源