为什么建议按固定顺序排查
Play Protect 的风险提示通常不是单一原因造成的,它可能来自静态特征、运行时行为、下载来源信誉,也可能是签名或包体被改动。如果一上来就加固、混淆、换渠道包,往往只是把触发点藏起来,下一次扫描还会复现。更稳的做法是把排查拆成四层,从成本最低、最容易验证的一层开始,逐层排除并留下证据。
下面这条顺序的核心逻辑是:先确认“应用要了什么”,再确认“代码做了什么”,然后确认“数据流向哪里”,最后确认“包还是不是你签的那个包”。前两步多数能在本地完成,后两步需要抓包和校验工具配合。
第一步:权限与敏感 API 是否与功能匹配
先导出当前线上包的权限清单和 manifest 声明,和产品实际功能逐条对照。重点看这几类:
- 短信、通话记录、通讯录、位置(尤其后台位置)、无障碍服务、设备管理、悬浮窗。
- Android 11 之后的软件包可见性声明,是否申请了过宽的 QUERY_ALL_PACKAGES。
- 是否存在声明了但代码里从未使用的权限,这类“冗余权限”在审核和扫描里都是负面信号。
排查方法:用 aapt 或构建产物里的 merged manifest 导出权限列表,再全局搜索对应 API 的调用点。发现“声明了但没用到”的,直接删;发现“用到了但用户看不懂”的,评估能否改成按需申请或换成更窄的替代方案。这一步的产出应该是一张权限对照表,标明每一项的用途、申请时机和是否可降级。
第二步:第三方 SDK 的行为与版本
权限问题排除后,下一层是 SDK。很多报风险来自集成进来的广告、统计、推送、热更新类 SDK:它们可能动态加载代码、读取设备标识、在后台自启动,或者版本过旧带有已知问题。
- 列出全部依赖,标出每个 SDK 的版本、来源(官方仓库还是手动引入的 aar/jar)。
- 对手动引入的 aar/jar 做一次反编译抽查,确认没有额外的权限申请、动态 dex 加载或反射调用敏感 API。
- 检查是否存在热更新、插件化框架,这类能力本身容易被判定为高风险行为,需要确认是否真的必要。
- 把 SDK 升级到官方仍在维护的版本,删除长期无更新的库。
这一步建议保留依赖清单和版本变更记录,后续如果再次触发提示,可以直接对比差异。涉及多渠道分发的团队,可以把包体管理和版本核对流程放在 服务页 描述的体系里统一管理,避免不同渠道包混用旧 SDK。
第三步:网络传输特征
权限和 SDK 都干净,仍然可能因为网络行为被标记:明文 HTTP 传输、证书校验被全局关闭、请求域名信誉差、上传了不该上传的设备信息。
- 抓一次完整启动和核心流程的包,确认没有明文 HTTP 请求,敏感接口全部走 HTTPS。
- 检查是否全局信任所有证书(TrustManager 空实现、HostnameVerifier 恒返回 true),这类代码是典型风险特征。
- 核对上报字段,设备标识、已安装应用列表、剪贴板内容这类数据是否被采集并外发。
- 确认请求域名不是已知的灰产或高风险托管地址。
抓包时注意区分调试包和线上包,很多问题只出现在某个渠道的配置里。把抓包结论整理成“域名—用途—是否加密—上报字段”四列,问题会非常直观。
第四步:签名完整性与包体一致性
最后一层是包本身。签名被替换、二次打包、v1/v2/v3 签名方案不一致,都可能让 Play Protect 对来源产生怀疑。
- 用 apksigner 校验签名,确认签名证书指纹与官方发布证书一致。
- 确认使用的签名方案组合,新目标版本应启用 v2 及以上,避免只依赖已弱化的 v1。
- 对比线上包与构建产物的哈希,确认分发链路中没有被中间环节重新签名。
- 检查是否使用了调试证书或共享证书打包正式版本。
如果发现签名不一致,优先查分发和托管环节,而不是重新签一个包了事。签名体系一旦混乱,后续所有排查都失去基准。关于证书与分发链路的规范,可以参考 关于我们 中的说明。
把结论沉淀成检查清单
四层排查完成后,建议把每一步的结论写成固定模板:权限对照表、SDK 版本清单、抓包字段表、签名指纹记录。这样下次再遇到 Play Protect 提示,团队不必从零开始,而是按清单逐项打勾,几分钟就能定位到变化点。真正的安全整改不是把提示消掉,而是让每一次发版都能说清楚:这个包要了什么权限、带了哪些 SDK、数据发往哪里、由谁签名。