21、Android 7.0+网络安全变化:网络安全配置变更、用户证书信任策略、Network Security Config

说实话,Android 7.0(API 24)这个版本,对搞安全的人来说是个分水岭。

我记得刚升级那会儿,公司好几个同事跑来问我:「怎么我的抓包工具突然不好使了?」

嗯,原因很简单——Google 在这一版里,彻底改了证书信任策略。以前那种「装个用户证书就能抓所有 HTTPS 流量」的好日子,到头了。

一、核心变化:用户证书不再被系统信任

Android 7.0 之前,你只要把抓包工具的 CA 证书装到手机里,系统就会默认信任它。所有 App 的 HTTPS 流量,你都能看得一清二楚。

但 7.0 之后呢?系统只信任预装在系统分区里的证书,以及通过 Network Security Config 明确指定的证书。

说白了,用户安装的证书——不管是你自己装的,还是用户自己点的——默认情况下,App 都不认。

关键点:

  • 用户证书(User CA):不再被系统默认信任
  • 系统证书(System CA):仍然被信任,但需要 root 权限才能安装
  • App 可以通过 Network Security Config 自定义信任策略

为什么会这样?

你想想看,如果随便装个证书就能监控所有 App 的流量,那用户的隐私还有什么保障?Google 这个改动,说白了就是堵死了中间人攻击的「傻瓜式」入口。

二、Network Security Config 是什么?

Network Security Config,简称 NSC。它是一个 XML 文件,放在 res/xml/ 目录下,用来告诉系统:「我这个 App 该信任哪些证书、走什么协议、要不要做证书绑定。」

我个人习惯,每个新项目都会配一个 NSC。哪怕暂时用不到,也先留个空壳。为什么?因为后面加功能的时候,不用再改 AndroidManifest,省事。

配置方式很简单,先在 AndroidManifest.xml 里引用:

<application
    android:networkSecurityConfig="@xml/network_security_config"
    ...>

然后在 res/xml/network_security_config.xml 里写具体规则:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </domain-config>
</network-security-config>

这段配置什么意思?

  • 全局默认:禁止明文 HTTP,只信任系统证书
  • 针对 example.com:允许明文,并且信任用户证书

我在项目中遇到过一种情况:测试环境用的是自签名证书,线上环境是正规 CA。如果不用 NSC,你就得在测试包和正式包之间来回改代码。有了 NSC,一个 domain-config 就搞定了。

三、用户证书信任策略

Android 7.0 之后,用户证书的信任策略变成了「按需启用」。

什么意思呢?就是 App 开发者必须主动声明:「我允许用户证书对我的流量进行解密。」否则,就算用户装了抓包证书,App 也不会理它。

具体来说,有三种信任模式:

模式 配置方式 适用场景
仅系统证书 <certificates src="system" /> 生产环境,安全性最高
系统 + 用户证书 <certificates src="system" />
<certificates src="user" />
调试环境,方便抓包
仅用户证书 <certificates src="user" /> 自签名证书场景,极少用

小技巧:调试包和正式包可以用不同的 buildType 来加载不同的 NSC 文件。比如 debug 版本信任用户证书,release 版本只信任系统证书。这样既不影响开发效率,也不降低线上安全性。

四、避坑指南:我曾经踩过的坑

讲几个我实际遇到过的坑,你们可以参考一下。

坑一:debug 包能抓包,release 包抓不了

我曾经排查过一个线上问题,用户反馈某个功能在 debug 包上正常,release 包上就报网络错误。查了半天,发现 debug 包用了 certificates src="user",而 release 包只信任系统证书。用户手机里装了个抓包证书,debug 包认了,release 包不认,结果就崩了。

解决方案?release 包加上 certificates src="system" 就行。但要注意,别把 user 也加进去。

坑二:Android 9 之后明文 HTTP 默认禁止

Android 9(API 28)又加了一条限制:cleartextTrafficPermitted 默认是 false。如果你还在用 HTTP 接口,记得在 NSC 里显式声明允许。

嗯,这个坑我踩过两次。第一次是忘了配,第二次是配了但域名写错了。

坑三:证书绑定(Certificate Pinning)别写死

有些团队喜欢在代码里写死证书的 SHA256 值。我建议别这么干。证书过期、更换的时候,你就得发版。用 NSC 的 pin-set 来做,至少还能通过热修复来更新。

警告:千万不要在生产环境里信任用户证书!除非你有非常特殊的理由。否则,你的 App 就等于给中间人攻击开了绿灯。

五、知识体系结构图

下面这张图,把 Android 7.0+ 的网络安全变化梳理了一下。你可以把它当成一个思维导图来看。

Android 7.0+ 网络安全变化 核心变化 用户证书不再被系统默认信任 引入 Network Security Config 机制 Android 9+ 默认禁止明文 HTTP 仅信任系统证书 按域名配置信任策略 证书绑定(Pinning) 目标:提升应用通信安全性,防止中间人攻击

六、总结

Android 7.0+ 的网络安全变化,说白了就是一件事:把证书信任的控制权,从用户手里交还给了 App 开发者

以前用户装个证书就能抓所有 App 的包,现在不行了。开发者必须主动配置 NSC,才能决定哪些流量可以被解密。

这对我们做安全的人来说,其实是好事。因为我们可以更精细地控制 App 的通信安全,而不是把命运交给用户手里的那台手机。

嗯,最后提醒一句:NSC 的配置,一定要跟你的 buildType 走。debug 和 release 分开,别混用。我见过太多人因为图省事,直接把用户证书信任开到了线上,结果被安全团队追着打。


公众号:蓝海资料掘金营,微信deep3321