如何禁用 WebView 的 JavaScript 弹窗和文件访问以提升安全性?

解读

面试官抛出此题,核心想验证两点:

  1. 是否真正踩过“WebView 被恶意 js 弹窗钓鱼”或“file:// 协议穿越”这类线上坑;
  2. 能否把官方 API、系统差异、国内 ROM 特性和业务场景打包成一套“可落地的安全方案”,而不是背文档。
    国内市场对隐私合规趋严(工信部 164 号文、App 违法违规收集使用个人信息行为认定办法),WebView 若可被 js 任意弹窗或访问私有文件,直接触碰“违规收集/违规使用”红线,应用下架风险极高。因此回答必须体现“代码+配置+灰度+验证”的闭环。

知识点

  1. WebView 双对象模型:WebView 自身设置 + WebSettings 设置,二者分离。
  2. JS 弹窗三通道:alert()/confirm()/prompt() 由 ChromeClient 回调拦截;onJsAlert/onJsConfirm/onJsPrompt 若返回 true 则系统不再弹窗。
  3. 文件访问三协议:file://、content://、asset://,其中 file:// 在 Android 7.0 之后默认禁止跨域,但仍可被 js 通过 <input type="file"> 唤起系统文件选择器。
  4. 国内特殊场景:
    • 微信、QQ 内置 WebView 为 X5 内核,API 与系统 WebView 略有差异,需运行时反射判断;
    • 部分 OEM ROM(MIUI、ColorOS)在 Android 10 以下仍允许 file:// 共享同一 UID 目录,需显式关闭。
  5. 合规审计点:静态扫描工具(如华为 AppGallery 云测、OPPO 安全扫描器)会检查 setAllowFileAccess、setJavaScriptEnabled 布尔值,若与隐私政策描述不符直接驳回上架。

答案

线上可落地的四步方案,直接背下来即可:

步骤 1:禁用 JS 弹窗
在自定义的 WebChromeClient 里空实现三大回调并返回 true,彻底接管弹窗:

webView.webChromeClient = object : WebChromeClient() {
    override fun onJsAlert(view: WebView, url: String, message: String, result: JsResult): Boolean {
        result.confirm(); return true  // 消费事件,系统不再弹窗
    }
    override fun onJsConfirm(view: WebView, url: String, message: String, result: JsResult): Boolean {
        result.cancel(); return true
    }
    override fun onJsPrompt(view: WebView, url: String, message: String, defaultValue: String, result: JsPromptResult): Boolean {
        result.cancel(); return true
    }
}

步骤 2:关闭文件系统访问
针对 Android 7.0 及以上,显式关闭 file 协议与内容访问:

webView.settings.apply {
    allowFileAccess = false          // 禁用 file://
    allowFileAccessFromFileURLs = false
    allowUniversalAccessFromFileURLs = false
    allowContentAccess = false       // 禁用 content://
}

若业务必须加载本地 html(如隐私协议页),采用“应用私有目录 + WebViewAssetLoader”方案,将静态资源映射到 https://appassets.androidplatform.net,彻底摆脱 file://。

步骤 3:屏蔽文件选择器
重写 WebChromeClient 的 onShowFileChooser,直接返回 false,阻断 H5 通过 <input type="file"> 唤起系统文件选择:

override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams): Boolean {
    return false
}

步骤 4:灰度验证

  1. 在内部搭建恶意测试页,嵌入 alert()+file 协议读取 /data/data/包名/ 的脚本;
  2. 使用 UiAutomator 自动点击,确认无系统弹窗、无文件内容泄漏;
  3. 上线前通过华为、小米、OPPO 三家云测,确保扫描报告 0 警告。

拓展思考

  1. 如果业务需要保留 JS 弹窗但想自定义 UI,可在 onJsAlert 中弹出 Compose AlertDialog,并通过 rememberSaveable 记录用户选择,实现“原生一致性”体验。
  2. 对于车载或 Wear 这类无文件管理器的场景,可将 WebView 运行在独立进程,并在 AndroidManifest 中配置 android:isolatedProcess=true,即使允许 file 协议也无法访问主进程数据,形成“沙盒中的沙盒”。
  3. 未来隐私沙盒(Privacy Sandbox on Android)限制跨站点跟踪,WebView 可能会引入新的 Topics API 与 FLEDGE 广告竞价,禁用 JS 弹窗的同时需关注新权限 AD_ID 与 Topics 权限的声明,否则在 Google Play 审核会被认定为“未声明却调用”,导致下架。