网络安全配置

通过网络安全配置功能,您可以在安全的声明性配置文件中自定义应用的网络安全设置,而无需修改应用代码。这些设置可针对特定网域和特定应用进行配置。此功能的主要功能包括:

  • 自定义信任锚点:自定义应用安全连接信任的证书授权机构 (CA)。例如,信任特定的自签名证书,或限制应用信任的公共 CA 集。
  • 仅限调试的替换:安全地调试应用中的安全连接,而不会增加已安装基数的风险。
  • 明文流量选择停用:保护应用免遭意外使用明文(未加密)流量。
  • 证书透明度选择启用:限制应用的安全连接使用可证明已记录的证书。
  • 证书锁定:将应用的安全连接限制为特定证书。

添加网络安全配置文件

网络安全配置功能使用 XML 文件来指定应用的设置。您必须在应用的清单中添加一个条目,以指向此文件。以下清单中的代码摘录演示了如何创建此条目

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

自定义受信任的 CA

您可能希望您的应用信任一组自定义的 CA,而不是平台默认的 CA。最常见的原因如下:

  • 连接到使用自定义 CA 的主机,例如自签名 CA 或公司内部颁发的 CA。
  • 将 CA 集限制为您信任的 CA,而不是所有预装的 CA。
  • 信任系统中未包含的其他 CA。

默认情况下,所有应用的安全连接(使用 TLS 和 HTTPS 等协议)都会信任预安装的系统 CA,而以 Android 6.0(API 级别 23)及更低版本为目标的应用也会默认信任用户添加的 CA 存储区。您可以使用 base-config(用于应用范围的自定义)或 domain-config(用于每个网域的自定义)自定义应用连接。

配置自定义 CA

您可能需要连接到使用自签名 SSL 证书的主机,或连接到其 SSL 证书由您信任的非公共 CA(例如您公司的内部 CA)颁发的主机。以下代码摘录演示了如何在 res/xml/network_security_config.xml 中为自定义 CA 配置应用

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

以 PEM 或 DER 格式将自签名或非公共 CA 证书添加到 res/raw/my_ca

限制受信任 CA 的集合

如果您不希望应用信任系统信任的所有 CA,则可以指定一个精简的 CA 集合。这可以保护应用免受其他任何 CA 颁发的欺诈性证书的影响。

限制受信任 CA 集合的配置类似于为特定网域信任自定义 CA,只是资源中提供了多个 CA。以下代码摘录演示了如何在 res/xml/network_security_config.xml 中限制应用受信任 CA 的集合

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example.com</domain>
        <trust-anchors>
            <certificates src="@raw/trusted_roots"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

以 PEM 或 DER 格式将受信任的 CA 添加到 res/raw/trusted_roots。请注意,如果您使用 PEM 格式,文件必须包含 PEM 数据,不得包含额外文本。您还可以提供多个 <certificates> 元素,而不是一个。

信任其他 CA

您可能希望您的应用信任系统中不受信任的其他 CA,例如系统尚未包含该 CA,或该 CA 不符合纳入 Android 系统的要求。您可以使用以下代码摘录在 res/xml/network_security_config.xml 中为配置指定多个证书源。

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

配置用于调试的 CA

调试通过 HTTPS 连接的应用时,您可能希望连接到没有生产服务器 SSL 证书的本地开发服务器。为了在不修改应用代码的情况下支持此操作,您可以使用 debug-overrides 指定仅用于调试的 CA,这些 CA 仅在 android:debuggabletrue 时才受信任。通常,IDE 和构建工具会自动为非发布版本设置此标志。

这比通常的条件代码更安全,因为出于安全考虑,应用商店不接受标记为可调试的应用。

以下摘录显示了如何在 res/xml/network_security_config.xml 中指定仅用于调试的 CA

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cas"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

选择启用证书透明度

注意:证书透明度支持仅在 Android 16(API 级别 36)及更高版本中可用。

证书透明度 (CT, RFC 6962) 是一项互联网标准,旨在增强数字证书的安全性。它要求 CA 将所有颁发的证书提交到公共日志进行记录,从而提高证书颁发过程的透明度和问责制。

通过维护所有证书的可验证记录,CT 使恶意行为者伪造证书或 CA 错误颁发证书变得更加困难。这有助于保护用户免受中间人攻击和其他安全威胁。如需了解更多信息,请参阅 transparency.dev 上的说明。如需了解有关 Android 上 CT 合规性的更多信息,请参阅 Android 的 CT 政策

默认情况下,无论证书是否记录在 CT 日志中,都会被接受。要确保您的应用仅连接到具有 CT 日志中记录的证书的目标,您可以选择启用该功能,方式可以是全局启用按网域启用

选择停用明文流量

注意:本部分的指导仅适用于以 Android 8.1 (API 级别 27) 或更低版本为目标的应用。从 Android 9 (API 级别 28) 开始,明文支持默认处于停用状态。

如果您希望您的应用仅使用安全连接连接到目标,则可以停用对这些目标的明文支持(使用未加密的 HTTP 协议而不是 HTTPS)。此选项有助于防止由于后端服务器等外部来源提供的 URL 更改而导致应用意外出现回归。有关更多详细信息,请参阅 NetworkSecurityPolicy.isCleartextTrafficPermitted()

例如,您可能希望您的应用确保与 secure.example.com 的连接始终通过 HTTPS 完成,以保护敏感流量免受恶意网络的攻击。

以下摘录显示了如何在 res/xml/network_security_config.xml 中选择停用明文

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">secure.example.com</domain>
    </domain-config>
</network-security-config>

锁定证书

通常,应用信任所有预安装的 CA。如果其中任何一个 CA 颁发欺诈性证书,应用将面临路径攻击者的风险。某些应用选择通过限制其信任的 CA 集合或通过证书锁定来限制其接受的证书集合。

证书锁定是通过提供一组公共密钥哈希(X.509 证书的 SubjectPublicKeyInfo)的证书来完成的。然后,证书链仅当其包含至少一个锁定的公共密钥时才有效。

请注意,在使用证书锁定功能时,您应始终包含一个备份密钥,这样,如果您被迫切换到新密钥或更改 CA(当锁定到 CA 证书或该 CA 的中间证书时),您的应用连接性不会受到影响。否则,您必须推送应用更新以恢复连接性。

此外,还可以为锁定设置过期时间,在该时间之后将不执行锁定。这有助于防止未更新的应用出现连接问题。但是,为锁定设置过期时间可能会使攻击者绕过您的锁定证书。

以下摘录显示了如何在 res/xml/network_security_config.xml 中锁定证书

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

配置继承行为

特定配置中未设置的值会继承。此行为允许更复杂的配置,同时保持配置文件的可读性。

例如,domain-config 中未设置的值将从父级 domain-config(如果嵌套)或从 base-config(如果未嵌套)中获取。 base-config 中未设置的值将使用平台默认值。

例如,考虑一个情况,所有连接到 example.com 子网域的连接都必须使用一组自定义 CA。此外,允许向这些网域发送明文流量,连接到 secure.example.com 时除外。通过将 secure.example.com 的配置嵌套在 example.com 的配置中,trust-anchors 无需重复。

以下摘录显示了在 res/xml/network_security_config.xml 中如何进行这种嵌套

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="false">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>

配置文件格式

网络安全配置功能使用 XML 文件格式。文件的总体结构如下面的代码示例所示

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

    <domain-config>
        <domain>android.com</domain>
        ...
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
        <pin-set>
            <pin digest="...">...</pin>
            ...
        </pin-set>
    </domain-config>
    ...
    <debug-overrides>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </debug-overrides>
</network-security-config>

以下各部分描述了文件格式的语法和其他详细信息。

<network-security-config>

可包含
0 或 1 个 <base-config>
任意数量的 <domain-config>
0 或 1 个 <debug-overrides>

<base-config>

语法
<base-config cleartextTrafficPermitted=["true" | "false"]>
    ...
</base-config>
可包含
<trust-anchors> <certificateTransparency>
说明
未被 domain-config 覆盖的所有连接所使用的默认配置。

任何未设置的值都将使用平台默认值。

针对 Android 9 (API 级别 28) 及更高版本应用的默认配置如下:

<base-config cleartextTrafficPermitted="false">
    <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
</base-config>

针对 Android 7.0 (API 级别 24) 到 Android 8.1 (API 级别 27) 应用的默认配置如下:

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
</base-config>

针对 Android 6.0 (API 级别 23) 及更低版本应用的默认配置如下:

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>
        <certificates src="system" />
        <certificates src="user" />
    </trust-anchors>
</base-config>

<domain-config>

语法
<domain-config cleartextTrafficPermitted=["true" | "false"]>
    ...
</domain-config>
可包含
1 个或多个 <domain>
0 或 1 个 <certificateTransparency>
0 或 1 个 <trust-anchors>
0 或 1 个 <pin-set>
任意数量的嵌套 <domain-config>
说明
用于连接到特定目标的配置,由 domain 元素定义。

请注意,如果有多个 domain-config 元素覆盖一个目标,则使用最具体(最长)的匹配网域规则的配置。

<domain>

语法
<domain includeSubdomains=["true" | "false"]>example.com</domain>
属性
includeSubdomains
如果为 "true",则此网域规则匹配该网域及其所有子网域,包括子网域的子网域。否则,该规则仅适用于精确匹配。

<certificateTransparency>

语法
<certificateTransparency enabled=["true" | "false"]/>
说明
如果为 true,则应用将使用证书透明度日志来验证证书。当应用使用自己的证书(或用户存储区)时,该证书很可能不是公开的,因此无法使用证书透明度进行验证。默认情况下,这些情况下会停用验证。仍然可以通过在网域配置中使用 <certificateTransparency enabled="true"/> 来强制执行验证。对于每个 <domain-config>,评估遵循以下顺序:
  1. 如果启用了 certificateTransparency,则启用验证。
  2. 如果任何 <trust-anchors>"user" 或内联(即 "@raw/cert.pem"),则停用验证。
  3. 否则,依赖于继承的配置

<debug-overrides>

语法
<debug-overrides>
    ...
</debug-overrides>
可包含
0 或 1 个 <trust-anchors>
说明
android:debuggable"true" 时应用的替换配置,这通常是 IDE 和构建工具生成的非发布版本的情况。debug-overrides 中指定的信任锚点将添加到所有其他配置中,并且当服务器的证书链使用这些仅用于调试的信任锚点之一时,不执行证书锁定。如果 android:debuggable"false",则此部分将完全被忽略。

<trust-anchors>

语法
<trust-anchors>
...
</trust-anchors>
可包含
任意数量的 <certificates>
说明
用于安全连接的信任锚点集合。

<certificates>

语法
<certificates src=["system" | "user" | "raw resource"]
              overridePins=["true" | "false"] />
说明
用于 trust-anchors 元素的 X.509 证书集合。
属性
src
CA 证书的来源。每个证书可以是以下之一:
  • 指向包含 X.509 证书的文件的原始资源 ID。证书必须以 DER 或 PEM 格式编码。对于 PEM 证书,文件不得包含额外的非 PEM 数据,例如注释。
  • 预安装系统 CA 证书的 "system"
  • 用户添加的 CA 证书的 "user"
overridePins

指定此来源的 CA 是否绕过证书锁定。如果为 "true",则不会对由此来源的某个 CA 签名的证书链执行锁定。这对于调试 CA 或测试对应用安全流量的中间人攻击可能很有用。

默认值为 "false",除非在 debug-overrides 元素中指定,在这种情况下默认值为 "true"

<pin-set>

语法
<pin-set expiration="date">
...
</pin-set>
可包含
任意数量的 <pin>
说明
一组公钥锁定。要使安全连接受信任,信任链中的一个公钥必须在锁定集合中。有关锁定的格式,请参阅 <pin>
属性
expiration
锁定过期的日期,格式为 yyyy-MM-dd,此日期之后将停用锁定。如果未设置此属性,则锁定不会过期。

过期时间有助于防止未更新其锁定集的应用出现连接问题,例如当用户停用应用更新时。

<pin>

语法
<pin digest=["SHA-256"]>base64 encoded digest of X.509
    SubjectPublicKeyInfo (SPKI)</pin>
属性
digest
用于生成锁定的摘要算法。目前,仅支持 "SHA-256"