证书锁定速查表¶
简介¶
证书锁定速查表是一份关于实现证书和公钥锁定的技术指南,正如 Jeffrey Walton 在弗吉尼亚分会演讲保护移动空间中的无线信道中讨论的那样。本指南旨在为在恶意行为者可能存在且信任会议可能成为负累的敌对环境中保护信道提供清晰、简单、可操作的指导。
问题是什么¶
用户、开发者和应用程序都期望其通信信道是安全的,但有些信道可能无法满足这一期望。如果基于证书的信任被滥用,使用SSL和TLS等知名协议构建的信道可能会容易受到中间人(MITM)攻击。恶意攻击有两种形式
- 攻击者能够以受害者网站的名义,从受信任的证书颁发机构(CA)获取一个伪造的数字证书;
- 攻击者能够将危险的CA注入到客户端的信任存储中。
在后一种情况下,有权更新信任存储的攻击者将有权更改移动应用程序的运作方式,这可能会破坏锁定机制。
正如证书和公钥锁定所阐明的那样,由于证书颁发机构和浏览器社区多年的安全进步,这个问题非常小。
什么是证书锁定¶
锁定是将主机与其预期的X509证书或公钥相关联的过程。一旦某个主机的证书或公钥已知或被发现,该证书或公钥就会与该主机关联或“锁定”。如果允许多个证书或公钥,则程序会持有一个锁定集(取自Jon Larimer和Kenny Root的Google I/O演讲)。在这种情况下,宣称的凭据必须与锁定集中的一个元素匹配。
何时添加锁定¶
主机或服务的证书或公钥可以在开发时添加到应用程序中,也可以在首次遇到证书或公钥时添加(这种方法通常称为“首次使用时信任”,即TOFU),或者可以通过未锁定的信道实时添加和更新。前者——在开发时添加——更受青睐,因为带外预加载证书或公钥通常意味着攻击者无法污染锁定。
请记住,这个“何时”是指你在哪个时间点进行锁定。第一个问题应该是,“我应该锁定吗?”。答案可能是永不。
何时执行证书锁定¶
几乎没有情况需要你考虑锁定。鉴于安全方面的进步,服务中断的风险几乎总是大于任何安全风险。如果你考虑锁定,你应该阅读证书和公钥锁定并充分理解威胁模型。
何时不进行锁定?¶
- 如果你无法控制连接的客户端和服务器端,请不要锁定。
- 如果你无法安全地更新锁定集,请不要锁定。
- 如果更新锁定集会造成中断,例如需要重新部署应用程序,那么可能不应锁定。(一个可能的例外是当你控制应用程序的重新部署时,例如在公司内部强制更新的情况下。)
- 如果证书密钥对在投入使用前无法提前预测,请不要锁定。
- 如果它不是原生移动应用程序,请不要锁定。
何时应用例外¶
如果你在一家实行“出口过滤”作为数据防泄漏(DLP)策略一部分的组织工作,你很可能会遇到拦截代理。我们喜欢将这些称之为“好的”坏角色(与“坏的”坏角色相对),因为它们都破坏了端到端安全,而且我们无法区分它们。在这种情况下,不要提议将拦截代理列入白名单,因为它会破坏你的安全目标。在风险接受部门的人员指示你这样做之后,将拦截代理的公钥添加到你的锁定集中。
如何进行锁定¶
其理念是重用现有的协议和基础设施,但以更强固的方式使用它们。为了重用,程序会继续执行其在建立安全连接时常做的事情。
为了加强信道,程序会利用库、框架或平台提供的OnConnect
回调。在回调中,程序会通过验证远程主机的证书或公钥来验证其身份。请参见下面的一些示例。
应该锁定什么¶
为了决定应该锁定什么,你可以遵循以下步骤。
- 决定是锁定根CA、中间CA还是叶证书
- 锁定根CA通常不推荐,因为它会大大增加风险,因为它意味着也信任其所有中间CA。
- 锁定特定的颁发或中间CA可以降低风险,但应用程序也将信任该CA或子CA颁发的任何其他证书,而不仅仅是为你的应用程序颁发的证书。
- 锁定叶证书是推荐的做法,但必须包括备用(例如中间CA或包含备用项的锁定集)。这提供了100%的确定性,即应用程序只信任其设计连接的远程主机,同时增加了故障转移或证书轮换的弹性。
例如,应用程序锁定远程端点的叶证书,但包含中间CA的备用锁定。这通过信任更多证书颁发机构增加了风险,但减少了应用崩溃的可能性。如果叶证书有任何问题,应用程序总是可以回退到中间CA,直到你发布应用程序更新。
-
选择是锁定整个证书还是仅锁定其公钥。
-
如果你选择公钥,还有两个额外的选择
- 锁定
subjectPublicKeyInfo
。 - 锁定其中一个具体类型,例如
RSAPublicKey
或DSAPublicKey
。
下面将更详细地解释这三个选择。建议你锁定subjectPublicKeyInfo
,因为它包含公共参数(例如RSA公钥的{e,n}
)以及算法和OID等上下文信息。上下文有时会帮助你保持方向感,右侧的图显示了可用的额外信息。
证书¶
证书是最容易锁定的。你可以通过带外方式获取网站的证书,让IT人员将公司证书通过电子邮件发送给你,使用openssl s_client
检索证书等。在运行时,你可以在回调中检索网站或服务器的证书。在回调中,你将检索到的证书与程序中嵌入的证书进行比较。如果比较失败,则使方法或函数失败,在客户端记录并提醒最终用户。如果你的威胁模型需要锁定,请理解用户会点击跳过任何警告,因此不要给用户提供继续和绕过锁定的选项。
优点
- 它可能比其他方法更容易实现,尤其是在Cocoa/CocoaTouch和OpenSSL等语言中。
缺点
- 如果网站定期轮换其证书,那么你的应用程序需要定期更新。如果你无法控制此证书何时投入使用,那么锁定将导致服务中断。
公钥¶
公钥锁定更灵活,但由于从证书中提取公钥需要额外的步骤,所以操作起来稍微复杂一些。与证书一样,程序会将提取出的公钥与其嵌入的公钥副本进行检查。考虑到目前大多数证书的有效期只有90天,使用公钥锁定还可以延长更新锁定集的时间线,因为你可以锁定一个尚未颁发证书的密钥。
优点
- 它允许访问公钥参数(例如RSA公钥的
{e,n}
)以及算法和OID等上下文信息。 - 它比证书锁定更灵活。可以在证书颁发很久之前计算锁定,如果策略允许,可以使用相同的密钥续订证书,以避免破坏锁定。后者是一种不好的密钥管理实践,只应在紧急情况下使用。
缺点
- 处理密钥(相对于证书)可能更困难,因为你必须从证书中提取密钥。提取在Java和.Net中只是一个小小的不便,但在iOS Cocoa/CocoaTouch框架和OpenSSL中则不太方便。
- 一些服务提供商在续订时会生成新密钥,这使得预缓存变得不可能。
哈希¶
虽然上面三种选择都使用了DER编码,但使用信息的哈希值也是可以接受的。事实上,最初的示例程序是使用摘要证书和公钥编写的。这些示例被修改,以允许程序员使用dumpasn1
和其他ASN.1解码器等工具检查对象。
优点
- 使用方便。许多库通常提供摘要证书指纹作为原生API。
- 哈希值小且长度固定。
缺点
- 无法访问公钥参数以及算法和OID等上下文信息,这些信息在某些用例中可能需要。
- 如果网站定期轮换其证书,那么你的应用程序需要定期更新。如果你无法控制此证书何时投入使用,那么锁定将导致服务中断。
证书锁定示例¶
本节讨论Android Java、iOS、.Net和OpenSSL中的证书和公钥锁定。代码为简洁起见已省略,但平台的主要要点已突出显示。
Android¶
自Android N以来,实现锁定的首选方法是利用Android的网络安全配置功能,该功能允许应用程序在不修改应用程序代码的情况下,在一个安全、声明性的配置文件中自定义其网络安全设置。
要启用锁定,可以使用<pin-set>
配置设置。
或者,你可以使用OkHTTP中的锁定等方法,以编程方式设置特定的锁定,正如OWASP移动安全测试指南(MSTG)和OKHttp文档中所解释的。
Android文档在未知CA实现文档中提供了一个如何在应用程序代码内自定义SSL验证(以实现锁定)的示例。然而,应避免从头开始实现锁定验证,因为实现错误极有可能发生,并且通常会导致严重的安全漏洞。
最后,如果你想验证锁定是否成功,请遵循OWASP移动安全测试指南(MSTG)中网络通信测试介绍以及Android特定网络测试章节的说明。
iOS¶
Apple建议通过在App Transport Security设置下的Info.plist
文件中指定CA公钥来锁定它。更多详细信息请参见文章“身份锁定:如何为你的应用程序配置服务器证书”。
有适用于iOS和macOS的开源SSL锁定库TrustKit可用。它提供了一个易于使用的API来实现锁定,并已在许多应用程序中部署。
此外,关于如何在iOS上自定义SSL验证(以实现锁定)的更多详细信息,请参阅HTTPS服务器信任评估技术说明。然而,应避免从头开始实现锁定验证,因为实现错误极有可能发生,并且通常会导致严重的安全漏洞。
最后,如果你想验证锁定是否成功,请遵循OWASP移动安全测试指南(MSTG)中网络通信测试介绍以及iOS特定网络测试章节的说明。
.Net¶
.Net中的锁定可以通过使用ServicePointManager
实现。示例可在OWASP MSTG中找到。
下载.Net示例程序。
OpenSSL¶
使用OpenSSL时,锁定可以在两个地方之一发生。首先是用户提供的verify_callback
。其次是通过SSL_get_peer_certificate
建立连接之后。这两种方法都允许你访问对等方的证书。
尽管OpenSSL执行X509检查,但在出错时你必须终止连接并关闭套接字。根据设计,不提供证书的服务器将导致X509_V_OK
且证书为NULL。要检查常规验证的结果
- 你必须调用
SSL_get_verify_result
并验证返回码为X509_V_OK
; - 你必须调用
SSL_get_peer_certificate
并验证证书为非NULL。
下载:OpenSSL示例程序。
Electron¶
electron-ssl-pinning,一个基于Electron应用程序的开源SSL锁定库。它提供了一个易于使用的API来实现锁定,还提供了一个根据所需主机获取配置的工具。
否则,你可以使用ses.setCertificateVerifyProc(proc)自行验证证书。
参考资料¶
- OWASP 注入理论
- OWASP 数据验证
- OWASP 传输层安全速查表
- OWASP 移动安全测试指南
- IETF RFC 1421 (PEM编码)
- IETF RFC 4648 (Base16、Base32和Base64编码)
- IETF RFC 5280 (Internet X.509, PKIX)
- IETF RFC 3279 (PKI, X509算法和CRL配置文件)
- IETF RFC 4055 (PKI, X509附加算法和CRL配置文件)
- IETF RFC 2246 (TLS 1.0)
- IETF RFC 4346 (TLS 1.1)
- IETF RFC 5246 (TLS 1.2)
- IETF PKCS #1: RSA密码学规范 2.2版