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+ 的网络安全变化,说白了就是一件事:把证书信任的控制权,从用户手里交还给了 App 开发者。
以前用户装个证书就能抓所有 App 的包,现在不行了。开发者必须主动配置 NSC,才能决定哪些流量可以被解密。
这对我们做安全的人来说,其实是好事。因为我们可以更精细地控制 App 的通信安全,而不是把命运交给用户手里的那台手机。
嗯,最后提醒一句:NSC 的配置,一定要跟你的 buildType 走。debug 和 release 分开,别混用。我见过太多人因为图省事,直接把用户证书信任开到了线上,结果被安全团队追着打。
公众号:蓝海资料掘金营,微信deep3321