通过网络安全配置功能,您可以在安全的声明性配置文件中自定义应用的网络安全设置,而无需修改应用代码。这些设置可针对特定网域和特定应用进行配置。此功能的主要功能包括:
- 自定义信任锚点:自定义应用安全连接信任的证书授权机构 (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:debuggable 为 true
时才受信任。通常,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>
,评估遵循以下顺序:- 如果启用了
certificateTransparency
,则启用验证。 - 如果任何
<trust-anchors>
为"user"
或内联(即"@raw/cert.pem"
),则停用验证。 - 否则,依赖于继承的配置。
- 如果启用了
<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"
。
-