Author:瘦蛟舞
Create:20180326
承接接上篇 https://xz.aliyun.com/t/2336
关键词:
安全性提升,更加有效覆盖对抗中间人攻击场景.
证书锁定本质是对抗中间人攻击.并非用于对抗破解抓包的.但如果程序逻辑未被注入运行在"可信环境"中倒是有些作用.
ssl对抗的攻击场景:
ssl pinning新增对抗场景:
因为发现赛门铁克签发了大量有问题的证书,Google官方博客公布了 Chrome 浏览器不信任赛门铁克证书的时间表:
2017 年 10 月发布的 Chrome 62 将在 DevTools 中加入对即将不受信任的赛门铁克证书的警告;
2017 年 12 月 1 日,DigiCert 将接手赛门铁克的证书签发业务;
2018 年 4 月 17 日发布的 Chrome 66 将不信任 2016 年 6 月 1 日之前签发的证书;
2018 年 10 月 23 日发布的 Chrome 70 将停止信任赛门铁克的旧证书.
受影响的赛门铁克 CA 品牌包括 Thawte、VeriSign、Equifax、GeoTrust 和 RapidSSL,几个独立运作密钥不受赛门铁克控制的次级 CA 得到了豁免,其中包括苹果和 Google.Google 建议使用赛门铁克证书的网站及时更新到受信任证书.
PM和开发让我给他讲下SSL Pinning,于是从中间人攻击和证书链开始balabla讲了一堆.
PM:哦,就是证书白名单吧
我:emmmm,是
开发:不想听你Balabala一堆,有没有简单的方法梭哈一把干?
我:emmmm,有
抽象业务场景分类如下:
单发APP推荐TrustKit的理由:
https://github.com/datatheorem/TrustKit-Android
https://github.com/datatheorem/TrustKit
理解文章可能需要如下知识.可以跳过此环节.
可信CA:CA(Certificate Authority)是数字证书认证中心的简称,是指发放、管理、废除数字证书的机构.CA的作用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行管理.
双向锁定:在客户端锁定服务端证书的基础上,服务端对客户端的证书也进行锁定,需要客户端再做一次证书预埋.多见于金融业务.
证书链:证书链就是Root CA签发二级Intermediate CA,二级Intermediate CA可以签发三级Intermediate CA,也可以直接签发用户证书.从Root CA到用户证书之间构成了一个信任链:信任Root CA,就应该信任它所信任的二级Intermediate CA,从而就应该信任三级Intermediate CA直至信任用户证书.
逐级验证:客户端对于收到的多级证书,需要从站点证书(leaf certificate)开始逐级验证, 直至出现操作系统或浏览器内置的受信任CA 根证书(root certificate).
通常逐级检测点如下:
先梳理下对证书处理的几种策略
首先考虑的锁定站点证书,这种策略安全性是肯定的,但是有个缺陷就是需要维护预埋证书.如果你没考虑过更新预埋证书会怎么样了?拿一个开源项目举例.
https://github.com/menjoo/Android-SSL-Pinning-WebViews (没错,又是它.之前SSL解锁的文章也是拿他举例.)
例子中的网站 https://www.infosupport.com/ 证书已经更新过一次,代码中的证书key是2015年的
而线上证书已于2017年更换,所以导致pinning失效,直接粗暴pinning可能导致业务无法访问.
现在的站点证书一般有效期在1到2年,所以做站点证书锁定还要保证服务可用性的话就得必须实现客户端锁定证书指纹的更新.
但是更新证书的网络请求该如何处理,有如下选择:
客户端的工作基本梳理完成,服务端需要实现证书指纹下发接口.还有每到证书即将过期的时候需要有人将的证书指纹配置进入.这里虽然提取指纹配置可以由代码实现,但是签发证书是由第三方CA完成的,所以离不开人对接.
整个锁定逻辑每隔一段时间(站点证书过期节点),需要有认为介入才能维持服务可用性.因为"人"这个X因素的引入会给业务稳定性带来极大风险,在大多数场景下不合理的.
先挂起这个安全性很高但是实现较为复杂且的方案,进入下一锁定策略.
锁定中间证书或根证书的优势是安全性接近锁定站点证书,且这两证书的有效期一般很长,很多都是15年到30年,所以暂不考虑热更新证书指纹.没准十几年后区块链去中心化就把CA给去掉了.
除证书有效期时间长的优势,锁定间证书或根证书还可以更好的兼顾业复杂的业务线,因为企业子域名很多情况下都是自己业务的站点证书,但是一个企业通常站点证书都是由一个中间证书(根证书)签下来.所以锁定间证书或根证书不用特别对每个业务线做调整,一套策略或者方案基本可以适用企业整个业务线.
先尝试中间证书的锁定方案,这里Comodo的中间证书超期时间为2029,距到期还有十来年超过了大多数的产品的生命中求.这么久的时间窗口,完全可以让指纹随着应用更新完成迭代.
但是锁定中间证书的方案会遇到一个问题,那就是更换证书CA(数字证书颁发机构).这就需要通过备份一些可能会用的到CA指纹.中间证书的量级相对于根证书要高出很多,而且也不好预测将来可能会更换到哪些中间证书.
先挂起这个安全性不错但是,冗余相对难操作的方案,进入下一锁定策略.
参考操作系统更新预埋CA根证书的机制,通过自升级完成锁定CA的指纹更新.Android N系统约内置了150多个系统根证书.而实际作为一个应用是不需要信任这么多CA的根证书的.可靠卖证书的CA就那么十来家,业务的安全需求决定了你需要哪类证书.这样备份证书的范围就收窄了,且根证书的数量级相对小.所以就没中间证书备份难的问题.
目前主流的SSL证书主要分为 DV < OV < EV,安全性和价格一次递增的.
DV和OV型证书最大的差别是:DV型证书不包含企业名称信息;而OV型证书包含企业名称信息,以下是两者差别对比表:
OV型和EV型证书,都包含了企业名称等信息,但EV证书因为其采用了更加严格的认证标准,浏览器会对EV证书"标绿"+"ID显示"(是的,VIP绿钻待遇,凸显尊贵身份).
综合看来根锁定策略的安全性,实施难度现在看来是比较适合账号业务的.接下来就是备份证书的选择.
备份锁定证书的主要的考量因素:
下图是win7的内置根证书,其中有相对效期较长CA分别有:Godaddy,DigiCert,Thawte.这三家在win7的这四个根证书均在2031年之后才生效.建议选择这类根作为备份,因为这些有效期长且在较早的系统版本中预埋,说明兼容性也过关.如果对安全性有更改追求可以预埋些EV证书比如DigiCert High Assurance EV Root CA.
其中GoDaddy Class 2 Certification Authority Root Certificate是交叉证书,根证书交叉链的示意图如下.
Godaddy G1和G2真实的交叉状态如下,也就是G1个G2两个根都能验过交叉中间证书签发的站点证书.这样情况建议将G1和G2的根均做预埋备份.
解决证书备份后问题,比如锁的根2031年到期,2031年之后应该如何处理.锁定超期是否应该拒绝连接?根据业务特性做选择:
android 8.0.0 下京东的锁定异常选择提示如下图 (android 6.0.1中没有此检测)
这个锁定方案相对前三个要保守许多,安全性提升也相对有限,所以业务代码出问题的概率也变的极小.
你需要做的仅仅是将通用操作系统中用户安装的第三方证书移除APP的证书信任列表.Android7.0后已经开始默认支持此特性,可以通过network-security-config更改配置.
在 Android 7.0 中,通过使用说明性“网络安全性配置”(而不是使用传统的易出错的编程 API,例如>X509TrustManager),应用可以安全地自定义其安全(HTTPS、TLS)连接的行为,无需任何代码修改.
支持的功能:
- 自定义信任锚:让应用可以针对安全连接自定义哪些证书颁发机构 (CA) 值得信赖.例如,信任特定的自签>署证书或限制应用信任的公共 CA 集.
- 仅调试重写:让应用开发者可以安全调试其应用的安全连接,而不会增加安装基础的风险.
- 明文流量选择退出:让应用可以防止自身意外使用明文流量.
- 证书固定:这是一项高级功能,让应用可以针对安全连接限制哪些服务器密钥受信任.
默认情况下,面向 Android 7.0 的应用仅信任系统提供的证书,且不再信任用户添加的证书颁发机构 (CA).如果面向 Android N 的应用希望信任用户添加的 CA,则应使用网络安全性配置以指定信任用户 CA 的方式.
完整用法请阅读
https://developer.android.com/preview/features/security-config.html
https://developer.android.com/training/articles/security-config
这里列举几个常用的配法.
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system"/>
</trust-anchors>
</base-config>
<debug-overrides>
<trust-anchors>
<certificates src="system"/>
<certificates src="user"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
<?xml version="1.0" encoding="utf-8"?> <network-security-config>
<!-- Official Android N 7.0 API -->
<domain-config>
<domain includeSubdomains="true">www.mi.com</domain>
<pin-set expiration="2034-06-30">
<!--GoDaddy Class 2 Certification Authority Root Certificate-->
<pin digest="SHA-256">VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=</pin>
<!--backup pin-->
<pin digest="SHA-256">VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=</pin>
</pin-set>
<!-- TrustKit Android API -->
<trustkit-config enforcePinning="true">
<!-- Add a reporting URL for pin validation reports -->
<report-uri>http://report.m.com/log_report</report-uri>
</trustkit-config>
</domain-config>
<debug-overrides>
<trust-anchors>
<!-- For debugging purposes, add a debug CA and override pins -->
<!--<certificates overridePins="true" src="@raw/debugca" />--> <certificates overridePins="true" src="user"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
设备ROM发版相对app发版要复杂许多,所以设备的证书锁定场景复杂性更高.这样先将设备抽象成两大类
如果设备是第一类通用操作系统比较好处理
-k, --insecure Allow connections to SSL sites without certs (H)
如果设备是第二类RTOS,首先得确认其是否支持SSL,其上运行的业务是否需要SSL.如果需要且支持,则可以通过自行预制根再参考前文完成锁定.
PinKit是一个android下简化的证书锁定工具类
PinKit方便SDK集成,不与network-security-config冲突.
网上代码大多取public key的hash进行base64 encod sha256当为Pin码,而浏览器上显示的是证书指纹的sha256.为了方便肉眼观察PinKit采用了同浏览器一致的证书指纹hex encode sha256当为Pin码
PinKit同TrustKit和AndroidPinning类似,都do the system's SSL validation : 先用系统的TrustManagerImpl和内置CA验一遍,再用锁定的CA验. (所以自签发证书这个库来锁需要注意下配置)
这样更为稳妥,至少有系统默认的一套为你兜底. 但是貌似有些臃肿,和其两次做hostNameVerified类似.
参考TrustKit完成SSLPinKitDemo如下
https://github.com/WooyunDota/SSLPinKitDemo
测试如何抓包了?
判断下app是debug版本还是release版本,debug版本不锁证书或者在锁定列表里加入一个测试证书.
做了根证书锁定如果换CA怎么办了?
如果只是系统证书锁定则不考虑此场景,如果是根证书锁定则需加入一些更换可能性较高CA的根证书指纹做备份,建议选择安全性较高EV证书,当然也会贵一些了.注意根证书的超期时间,选择时效长一些的.
webview中是的请求是否要做证书锁定?
不建议,不推荐在webview中做证书锁定.
证书吊销,失效等问题是否需要业务自己再实现一次?
不需要,方案复用系统TM检测.在系统TrustManger基础上收缩CA根证书的范围.
会影响正常用户的代理(梯子软件)的使用吗?
一般是没有影响的.
The basic X.509 v3 format described in ASN.1:
---------------------------------------------------------------------
-- X.509 signed certificate
---------------------------------------------------------------------
SignedContent ::= SEQUENCE
{
certificate CertificateSigned,
algorithm Object Identifier,
signature BITSTRING
}
---------------------------------------------------------------------
-- X.509 certificate to be signed
---------------------------------------------------------------------
CertificateToBeSigned ::= SEQUENCE
{
version [0] CertificateVersion DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name
validity Validity,
subject Name
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] Extensions OPTIONAL
}
https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e
https://developer.android.com/training/articles/security-config
https://msdn.microsoft.com/en-us/library/windows/desktop/bb540800(v=vs.85).aspx