记录一次 Android 内嵌 WebView 白屏无法加载内容的故事事故

昨天下午,产品经理突然发来了一张截图,内容是我们的 APP 因为违反了 Google Play 的某些规定被下架了。

这当然是马上排查原因并 fix 然后打包给测试同事重新测试一遍然后再提审啦。
但是问题来了,测试的同事发现 App 内的所有 HTML5 的页面都无法打开了,而主包(国内版本)却是正常的。

这怎么可能会发生呢,fix 被下架的问题并没有修改到 WebView 业务相关的代码,这就百思不得其解了。甚至一度以为是前端同事刚上线的代码影响到了海外版本的业务想甩锅。

拿起自己的手机装上 Google Play 版本的包打开对应的页面一看,基本上是第一次能正常加载,退出后再次点击进入 WebView 页面就无法加载了。

调试

  1. 先抓包看 HTTP Request 是否有返回数据,由于是 HTTPS 的页面,尝试使用 Charles 的 Enable SSL Proxy 解开 SSL 加密,无法解开(这里为自己挖了个坑),查看其他 HTTP 的页面也发现 HTML5 的页面 URL 返回的 Response 都是正常的。

  2. 尝试对比主包和海外版的 HTML5 页面的 HTTP Request 的区别,发现只有 User Agent 有所区别,试着将海外版的 User Agent 修改成和主包一样,结果相同,还是无法加载

  3. 只好尝试着调试 WebView
    首先打开 WebView 的 debug 模式

1
WebView.setWebContentsDebuggingEnabled(true);

将手机连接到电脑,打开 Chrome,输入 chrome://inspect 通过 Chrome DevTools 调试手机上的 WebView 页面
Chrome DevTools
进入 Devices Tab,看到自己的手机设备和当前打开的 HTML5 页面,点击 inspect 打开 DevTools ,此时可以看到有一个提示’安全错误’,并且在刷新重新加载的时候发现页面偶尔会一闪而过一个红色的页面。

一开始不以为然,以为是自己为了解开 HTTPS 而导致的就没放在心上,这时前端的同事让我给他装个可以调试的包给他排查一下问题,我把 apk 文件给到他后,在他的手机上打开 HTML5 的页面是都正常可以访问的。

这就更让人百思不得其解了。
红屏页面

排查

后来经测试的另一个同事提醒,说会不会是因为什么原因导致访问会提示不安全了

于是乎上网 Google 了一下 “您要访问的网站包含有害应用” 这个关键字无果,紧接着在其前面加上 WebView,搜出来了一篇 CSDN 上的 Blog,终于找到了问题所在。

自 2018 年 4 月起,随着 WebView 66 发布,Google Play 保护机制,将在 WebView 中默认开始此安全浏览策略。
而 Android 开发者在使用 WebView 时,无需再进行任何更改,即可享受此项保护服务。自 Android 8.0 开始,WebView 中即已经集成安全浏览功能,并且与 Android 版的 Chrome 采用相同的底层技术。
一旦触发 WebView 的安全机制,就会出现类似这样的“红屏”警告。

作者:承香墨影
链接:https://juejin.im/post/5c8899c56fb9a049b41d5432

来源:掘金

原来在 Android O 以后,WebView 的安全浏览策略(Google Safe Browsing) 会在 WebView 中默认开启,Google 自己会维护一份清单判断哪些网站是”有害的“提醒用户,并通过 Google Play Service 同步到用户设备中,而我们的域名刚好被 Google 认为是”有害的“,导致了我们的 HTML5 业务的页面无法打开。

而这也正好解决了我的疑问:为什么前端同事的手机可以正常打开,而我和测试同事的手机却不行,因为我和测试同事的手机为了测试海外版的支付功能都安装了 Google Play Service 而前端同事的手机没有安装 Google Play Service 。所以没有同步”有害网站“

知道了原因后解决起来就很容易了

解决

申诉

管理有关不安全网站的警告

请求审核的地址

代码中强制设置不使用『安全浏览策略』

  • Android O 以上

    webviewSetting.setSafeBrowsingEnabled(false)

    但这个方法只能用于 API 26 以上,如果在 API 26 一下,则需要在 AndroidManifest 中进行声明

  • Android O 以下
1
2
3
4
5
6
7
<manifest>
<application>
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="false" />
...
</application>
</manifest>

如果觉得这样一刀切的方案有点简单粗暴,WebView 还提供了一个设置白名单的方法,可以将业务上用到的域名列入应用的白名单

1
WebView.setSafeBrowsingWhitelist(List<String> hosts, ValueCallback<Boolean> callback)

甚至还可以在 WebView 中设置是否被安全机制拦截的监听回调,在接收到被安全拦截后,进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyWebViewClient : WebViewClient() {
// Automatically go "back to safety" when attempting to load a website that
// Google has identified as a known threat. An instance of WebView calls
// this method only after Safe Browsing is initialized, so there's no
// conditional logic needed here.
override fun onSafeBrowsingHit(
view: WebView,
request: WebResourceRequest,
threatType: Int,
callback: SafeBrowsingResponse
) {
// The "true" argument indicates that your app reports incidents like
// this one to Safe Browsing.
//在这里处理被安全浏览机制拦截
callback.backToSafety(true)
Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
}
}

最后感谢测试同事,又让我涨知识了

参考链接:

Android webview(安全策略) 出现 您要访问的网站包含有害应用(PS. 这篇文章最后的 AndroidManifest 中的写法是错误的)

WebView,我已经长大了,知道自己区分是否安全了!

最重要的!!!还是 Google 官方文档啊

管理 WebView 对象

Android 8.1 Features and APIs