Android network_security_config能否实现单端点免SSL Pinning其余强制固定
核心结论
仅通过Android原生network_security_config.xml无法实现你描述的路径级SSL Pinning差异化规则。
Android原生网络安全配置的规则最小匹配粒度是域名维度,不支持URI路径级的规则拆分:所有针对某个域名配置的pin-set规则,会对该域名下的所有请求路径全局生效,没有配置项可以单独豁免某一个路径的Pinning校验。
可落地的实现方案
如果要实现「同域名下特定路径跳过Pinning、其余路径强制Pinning」的需求,有两种成熟方案可选:
方案1:代码层实现SSL连接分流(服务端无需改动)
基于你使用的网络框架(比如OkHttp、Retrofit底层依赖的OkHttp)实现两套隔离的SSL校验逻辑:- 第一套SSL上下文配置你预设的证书公钥固定规则,作为默认校验逻辑
- 第二套SSL上下文走系统默认的CA证书校验逻辑,不做Pinning校验
- 在网络请求拦截层做URL判断:当请求路径匹配
example.com/access_without_pin时,强制走无Pinning的SSL连接;其余所有同域名请求,全部走带Pinning校验的连接
注意:两套SSL连接必须做完全隔离,不要共用连接池,避免连接复用导致Pinning规则被绕过。另外路径匹配规则要写得足够严格,避免匹配范围过大漏过本该校验的业务接口。
方案2:拆分专属子域名(推荐,稳定性最高)
如果服务端可以调整配置,是成本最低的实现方式:
将不需要Pinning校验的/access_without_pin端点单独部署到独立子域名(比如no-pin.example.com),就可以直接通过network_security_config.xml实现规则配置,不需要写自定义SSL逻辑,参考配置如下:<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="false"> <trust-anchors> <certificates src="system" /> </trust-anchors> </base-config> <!-- 主域名所有接口强制SSL Pinning --> <domain-config> <domain includeSubdomains="false">example.com</domain> <pin-set expiration="2026-12-31"> <pin digest="SHA-256">填入主域名证书对应的公钥SHA256值</pin> <pin digest="SHA-256">填入备用证书公钥SHA256值,避免证书轮换导致服务不可用</pin> </pin-set> </domain-config> <!-- 独立子域名不配置pin-set,走默认系统证书校验,无Pinning --> <domain-config> <domain includeSubdomains="false">no-pin.example.com</domain> </domain-config> </network-security-config>
避坑提示
不要尝试在同一个域名连接的X509TrustManager中做路径判断分流:SSL握手完成在HTTP请求行(包含请求路径)发送之前,同一条连接上的多个请求会复用SSL会话,这时候做路径判断会出现逻辑错乱,要么出现不该校验的路径被拦截,要么出现本该校验的路径绕过Pinning的安全问题。
内容的提问来源于stack exchange,提问作者Onur Arda Bodur Alumni

