网络安全配置(Network Security Config)

Android 7.0 引入的网络安全配置,说白了就是一套声明式的安全策略文件。你不需要在代码里写一堆 if-else 来判断证书、校验域名,直接在 XML 里配好就行。我个人觉得,这是 Android 在安全领域做得最舒服的一个设计——把复杂逻辑交给系统,开发者只关心规则。

为什么需要 Network Security Config?

在 Android 7.0 之前,App 的网络通信安全全靠开发者自己把控。我记得有个项目,团队里每个人写的 HTTPS 校验逻辑都不一样,有人用 HostnameVerifier,有人重写 TrustManager,最后测试阶段发现好几个接口连不上——因为证书校验写错了。

说白了,这种碎片化的安全实现方式,很容易出问题。Google 也意识到了这一点,所以在 Android 7.0(API 24)推出了 Network Security Config,让开发者通过 XML 文件统一声明安全策略。

核心思想: 将网络安全策略从代码中剥离,用声明式配置替代编程式实现。系统自动执行策略,开发者只需关注规则本身。

配置文件的位置与格式

配置文件必须放在 res/xml/ 目录下,文件名随意,比如 network_security_config.xml。然后在 AndroidManifest.xml<application> 标签中引用:

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

嗯,这里要注意:如果你没有配置这个属性,系统会使用默认的安全策略——也就是只信任系统预装 CA 证书,并且只允许 HTTPS 明文流量。

核心配置项详解

我习惯把 Network Security Config 的配置项分成三类:基础配置、证书信任配置、调试配置。咱们一个一个来看。

1. 基础配置:base-config

base-config 是全局默认配置,所有未明确指定的域名都会使用这里的规则。举个例子:

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

这段配置的意思是:所有请求默认使用 HTTPS,只信任系统证书。我在项目中一般都会把 cleartextTrafficPermitted 设为 false,除非有特殊需求——比如连接内网设备。

注意: 从 Android 9(API 28)开始,cleartextTrafficPermitted 默认就是 false。如果你还在用旧版本,建议显式声明,避免安全漏洞。

2. 域名特定配置:domain-config

实际项目中,不同域名往往需要不同的安全策略。比如你的 API 服务器用正式证书,但测试环境可能用自签名证书。这时候 domain-config 就派上用场了:

<domain-config cleartextTrafficPermitted="false">
    <domain includeSubdomains="true">api.example.com</domain>
    <trust-anchors>
        <certificates src="system" />
        <certificates src="user" />
    </trust-anchors>
</domain-config>

<domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="false">192.168.1.100</domain>
</domain-config>

你看,API 域名只信任系统证书和用户证书,而内网 IP 允许明文通信。这种粒度控制,在代码里实现起来很麻烦,但在 XML 里就是几行的事。

3. 调试配置:debug-overrides

这个功能我特别喜欢。开发阶段需要抓包调试,但正式版又不能用用户证书——怎么办?debug-overrides 就是为此设计的:

<debug-overrides>
    <trust-anchors>
        <certificates src="user" />
    </trust-anchors>
</debug-overrides>

这段配置只在 debug 构建中生效。你想想看,开发时用 Charles 抓包,只需要把 Charles 的证书装到手机上,然后信任用户证书就行。发布到应用商店时,debug 配置自动失效,用户证书不会被信任——安全又方便。

小技巧: 我曾经在 debug 模式下忘记配置 debug-overrides,结果抓包工具死活连不上。后来养成习惯:新建项目第一件事,就是配好 debug 证书信任规则。

证书固定(Certificate Pinning)

证书固定是 Network Security Config 的进阶用法。说白了,就是只信任你指定的那几个证书或公钥,其他证书一概不认。这样可以防止中间人攻击,即使 CA 被攻破,攻击者也无法伪造你的服务器证书。

<domain-config>
    <domain includeSubdomains="true">api.example.com</domain>
    <pin-set expiration="2025-12-31">
        <pin digest="SHA-256">47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=</pin>
        <pin digest="SHA-256">Gq8WJ7oP6hU5z4s3x2w1v0b9n8m7l6k5j4h3g2f1d0s=</pin>
    </pin-set>
</domain-config>

这里有两个关键点:

  • 至少两个 pin 值: 一个用于当前证书,一个作为备份。万一证书轮换,备份 pin 能保证服务不中断。
  • 设置过期时间: 避免 pin 值长期不变导致的安全风险。过期后,系统会回退到普通的证书校验逻辑。

避坑指南: 我曾经在项目中只固定了一个证书的 pin 值,结果证书到期更换后,所有用户都连不上服务器——因为新证书的 pin 值不在配置里。从那以后,我每次做证书固定都会留一个备份 pin,并且设置合理的过期时间。

SVG 流程图:Network Security Config 决策逻辑

Network Security Config 决策流程 发起网络请求 域名是否匹配 domain-config? 使用 domain-config 规则 使用 base-config 规则 是否 debug 构建? 合并 debug-overrides 规则 直接执行安全策略

实际项目中的配置模板

下面是我个人常用的配置模板,涵盖了大多数场景:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- 默认:只信任系统证书,禁止明文 -->
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <!-- API 服务器:证书固定 -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.example.com</domain>
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
        <pin-set expiration="2026-06-30">
            <pin digest="SHA-256">...主证书哈希...</pin>
            <pin digest="SHA-256">...备份证书哈希...</pin>
        </pin-set>
    </domain-config>

    <!-- 内网设备:允许明文 -->
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="false">192.168.0.0/16</domain>
        <domain includeSubdomains="false">10.0.0.0/8</domain>
    </domain-config>

    <!-- 调试模式:信任用户证书 -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

常见问题与避坑指南

  • 配置不生效: 检查 AndroidManifest.xml 中的 android:networkSecurityConfig 是否指向了正确的文件路径。我曾经犯过把文件名写错的低级错误。
  • 证书固定导致服务中断: 证书轮换时,务必先更新 App 的 pin 配置,再更换服务器证书。或者设置较长的过期时间,给用户留出更新 App 的缓冲期。
  • debug-overrides 在 release 构建中生效: 检查构建类型配置,确保 debug-overrides 只在 debug 构建中打包进去。可以用 buildConfigField 控制。
  • 明文流量被拦截: 如果某个域名需要 HTTP 通信,记得在 domain-config 中显式设置 cleartextTrafficPermitted="true"

我的习惯: 每次发布新版本前,我都会用 adb shell dumpsys netpolicy 检查当前 App 的网络安全策略是否生效。这个命令会显示所有已注册的 Network Security Config 规则,一目了然。

总结

Network Security Config 是 Android 安全通信的基础设施。它把复杂的证书校验、域名匹配、明文控制逻辑,简化成了几行 XML 配置。你想想看,以前要在代码里写几十行甚至上百行的 TrustManager,现在一个 <trust-anchors> 标签就搞定了。

我个人建议:所有新项目都使用 Network Security Config,不要再用代码实现安全策略。这样不仅代码更干净,而且系统会自动适配未来的安全更新——比如 Android 14 对证书校验的改进,你什么都不用改,直接受益。

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