注:本文为“小米安全中心”原创,作者: 那我就随便唱两句,转载请联系“小米安全中心”

上期回顾:自动化web安全测试

0x00 这是开篇

年初时候,网上有这么一篇文章,题目叫“加速HTTPS普及!谷歌Chrome要为所有HTTP网站打上红叉”,有兴趣的瓜众可以去Google一下。历史总是惊人的相似,上学的时候没有答好题要打红叉,现而今,网站没有部署HTTPS也要打红叉,形如图1。当然,要想变绿也可以,看到那些CA机构笑眯眯的拇指搓着中食指了吗?虽然需要破财,虽然我们代表朝鲜伊朗叙利亚强烈反对,但不妨碍这是大势所趋。

12306

图1

0x01 证书简介

说到HTTPS,就不得不提证书,而对于其中的TLS协议、加密算法细节等我们按下不表,毕竟文章要契题,更重要的是,这些细节目前我也不太懂啊。对于证书的一些概念,网上有很多资料可以帮助了解,可以根据具体细节需要进行查找,其中的术语我就不介绍了,起码PKI、X.509、CA是要了解下的,常见的pem、crt、cer、key、der等后缀文件是干什么的也是要了解了解的(请见参考资料1)。

证书一般由CA(根CA、中级CA等)机构签发,常见的CA机构如:Symantec、Comodo、Godaddy、GolbalSign 和 Digicert等,我们在一般证书信息中都可以看到,然而,对于我们自己给自己颁发证书的Sinorail Certification Authority,我表示很尴尬。大部分企业网站证书需要向这些CA购买,而且价格不菲,当然也有一些免费的证书可以申请,比如StartSSL,比如Let's Encrypt,这些免费证书个人网站用用无可厚非,但目前我还没见过哪个大点的公司在使用,当然不排除以后会用,尤其是Let's Encrypt,麻烦是麻烦了点,可是,省钱啊。

ssl1

图2

对于证书包含的内容,见图2:

图中这些信息都进行了一些调整,以便展示,具体会更加复杂一些。这些信息是后续对证书进行检测的前提。

0x02 证书检测

是的,到重点了。

你也看到了,获取服务器证书的信息并不困难,不就是鼠标点点点吗,然而,对一些企业内部大量域名逐一点击查看检查,这明显是智商不达标的行为。我可是程序员啊,所以重要的事情说三遍,自动化、自动化以及还是自动化。

说到这里,可能有朋友会问,网上不是有这类服务么,而且有的还提供了终端工具。你说的是sslabs(请见参考资料2)吧,这我怎么会不知道。功能很强大有没有,检测很全面有没有,但是,太慢了,且不可控。慢的主要因为有以下几点:

  1. 检测项太多。这个网站提供了相当全面的检测,包括协议、漏洞、加密套件以及模拟各种客户端类型的请求,以你智慧能想到的它基本都检测了,但是,我只需要检测证书相关啊,如同我要吃个苹果,结果给了果园让我等着秋天丰收。
  2. API限制。能提供API接口调用服务,而且还提供了各种语言的终端工具,这点我是很欣慰的,但是,美好的事物总是稍纵即逝 - 接口有限制,并发要限制、速率也要限制,具体见 Qualys API Limits(请见参考资料3)。

那么,我们的目标是什么呢?

  1. 速度要快。最起码的并发检测应该要有吧,毕竟我们也是有上万域名的企业。如果半分钟检测一条,我个人情感上是无法接受的。
  2. 目标专一。需要的我们要检测到,不需要的我们就不检测了。目前主要是检测证书是否正常,后续可能会添加其他检测项。
  3. 结果JSON化。检测结果要作为固定JSON格式返回,无论后续是格式化输出,存储,标准的JSON格式都是极好的。

既然目标已然这么明确,那么接下来就是定义什么会导致证书不可信,这是我们进行检测依据所在。原因是很多的,具体来说,我把它们分为三类(欢迎补充):

1.证书本身有问题

2.证书配置有问题

3.CA机构有问题

有了目标,知道了检测项,接下来就是如何进行检测,我们对于上述导致证书不可信的问题逐一进行检测。

是否激活、过期

这个是很好检测的。大部分编程语言都有证书相关的库(Python的话,我建议直接用Python 3会比较好,这方面一些有用的函数都是Python 2.7.9以后才有的),具体如何请求获取证书信息,因语言不同形式也可能有差别,但大体思路是一致的,具体参见所使用编程语言的手册,这里我是采用Go来实现,毕竟Go的标准库源码美观多了。需要注意的一点是,很多库本身是有一定证书合法性检查的,但是无法按我们的需求来进行检测,更为关键的是证书有问题会直接报错,无法获取证书信息。所以,无论采用什么语言,无论该证书是否合法,必须先要获取证书信息才能进行后续更详细的检测,因此这里要在语言层面忽略对证书合法性的检查,防止提前报错,具体到Go中就是(其他类似):

  1. conn, err := tls.DialWithDialer(dialer, "tcp", domain+":443", &tls.Config{
  2.    InsecureSkipVerify: true,  # 设置忽略证书安全性验证
  3. })

获取到证书信息就好办了。证书信息中会包含有两个日期,NotAfter表示过期日期,NotBefore表示激活日期。通过对比当前日期是否介于二者之间,就可以判断证书是否激活或者过期。而且还可以计算距离过期天数,快过期时,提前发送邮件提醒相关人员申请新的证书。

域名是否匹配

域名是否匹配其实也是很好检测的。因为,很多语言标准库里也有提供类似verifyHostname的函数来进行检测。但是对于那些没有提供该函数的情况该如何检测呢?好说,具体检测的逻辑如下:

  1. 如果这里的”域名“是IP,那么检测IP是否属于证书IP SANs(Subject Alternative Names)。
  2. 如果不是IP,那么检测是域名否属于证书的DNSNames。
  3. 如果不属于DNSNames,那么检测域名是否匹配颁发对象的CommonName。

如果上述三点都不匹配,那证书就可以GG了。注意:这里的匹配,对于IP而言就是绝对相等,而对于域名而言,要考虑通配符的情况,通配符匹配判定可以参见此处(请见参考资料4)* 匹配任何字段。

证书签名是否安全

具体到签名算法,我没有完整的列表,目前知道的,MD2-RSAMD5-RSASHA1-RSA 都被认为是不安全的签名,这里重点强调下SHA1-RSA,因为目前很多网站的证书任然采用(截至2015年5月,大约45%的数字证书皆使用此算法,SHA-1的证书将从2017开始不再被主流浏览器厂商视为安全的),说到这里我就很尴尬了,原因就不说了,欢迎到MiSRC提漏洞。

知道了以上这几点,也就方便进行检测了,获取证书的签名算法然后进行对比即可。

证书链的检测

对于证书链检测,主要是两方面,一是证书链中证书个体的检测,另外一个是证书链整体的检测。对于前者,与之前服务器证书检测一样,主要也是检测是否过期、签名是否安全等,外加是否属于自签名证书(即自己给自己签发,也就是SubjectKeyIdAuthorityKeyId相同)。对于证书链的检测,主要集中在以下几点:

  1. 证书链长度检测。既然都称为链了,那长度最起码也得大于2吧。而最长长度也得要有一定的限制,毕竟没有哪个浏览器证书链包含十几份证书,具体上限是多少,我没有找到相关资料,可以自行设定,根据后续检测结果调整。
  2. 证书链顺序检测。这里顺序检测主要是保证下级证书必须是由上级机构签发,即证书链中,下级证书的AuthorityKeyId要与上一级证书的SubjectKeyId相等。
  3. 证书链根证书检测。这里要进行根证书的可信检测,主要是指证书链的根部证书必须包含在已知的可信根证书集中,这是保证后续签发证书可信的必要条件。

证书链长度检测比较简单,编程获取的证书信息中都会包含证书链信息,一般以列表形式存在,只要检测其列表长度即可。

证书顺序的检测需要从服务端证书开始,依次检测该证书的AuthorityKeyId是否与列表中下一证书的SubjectKeyId一致,保证下级证书由上级机构签发。

  1. aKey := certs[0].AuthorityKeyId
  2. for _, c := range certs[1:] {
  3.    // simply check
  4.    if bytes.Compare(aKey, c.SubjectKeyId) != 0 {
  5.        paths["chainIssue"] = append(issues, "incorrect order")
  6.        break
  7.    }
  8.    aKey = c.AuthorityKeyId
  9. }

根证书可信的检测,主要是检测证书链中根部证书是否可信。浏览器和操作系统中一般都会自带大量可信CA根证书,例如:

操作系统 证书位置
Debian/Ubuntu/Gentoo /etc/ssl/certs/ca-certificates.crt
Fedora/RHEL /etc/pki/tls/certs/ca-bundle.crt
OpenSUSE /etc/ssl/ca-bundle.pem
OpenELEC /etc/pki/tls/cacert.pem

除此之外还有类似aosp.pemapple.pemmicrosoft.pemjava.pemmozilla.pem等作为补充,总之,可以包含你所知道的所有可信根证书集合,越全越好,甚至可以把那些浏览器不信任但我们认为可信的根证书也包含在内,比如SRCA。对于可信CA根证书的检测,只需确定证书链中的根证书是否属于所提供的可信CA根证书集即可,具体到细节实现,就是判断证书链根证书的SubjectKeyId是否包含在上述证书集组成的字典(key为SubjectKeyId,value可以为证书)中,当然其中需要一些细节处理。具体代码逻辑参见此处(请见参考资料5)

其他进一步检测

  1. 证书是否被吊销。通过OSCP进行查询,目前还未实现。
  2. 漏洞、协议、加密套件等方面的检测,这是后话。

0x03 还有谁

以上的检测手段只是在下自己摸索的一些方法,网上找不到类似可供参考的解决方案,谬误不可避免,毕竟我党摸着石头过河也是犯过很多错误的,如果有所帮助,我很欣慰,如果浪费你时间,怪我咯?这其中比较有参考价值的就是Go标准库中x509源码实现以及testssl.sh(请见参考资料6)。如果想要更精确的进行检测,可以考虑通读这几份源码以及相关的RFC,这个需要的时间就比较长了,有时间的朋友可以研究下,然后,完了记得回来指导纠正。

文章中文字描述较多,没有贴太多的代码,具体实现已放到Github(请见参考资料7)上,目前基本可用,缺少一些文档示例,编译运行后你会发现其结果与ssltest(请见参考资料8)检测中的Authentication部分很相似,那肯定啊,我就是照着它的功能来实现的,当然多少会有一些出入,因为目前还在等我们的精神元首明哥开发后台界面,以便进一步跟踪检测结果来修复调整。

好了,该说的说完了,去吧皮卡丘,传送门(请见参考资料9)

参考资料

  1. http://www.cnblogs.com/guogangj/p/4118605.html
  2. https://www.ssllabs.com/ssltest/
  3. https://www.qualys.com/docs/qualys-api-limits.pdf
  4. https://golang.org/src/crypto/x509/verify.go?s=12104:12156#L353
  5. https://github.com/jetz/cyssl/blob/master/plugins/certificate.go#L308%3aL361
  6. https://github.com/drwetter/testssl.sh/blob/master/testssl.sh
  7. https://github.com/jetz/cyssl
  8. https://www.ssllabs.com/ssltest
  9. https://github.com/jetz/cyssl

问题

以下是某著名网站SSL证书进行再编码后的密文(如此简单,我只能帮到你这里了),请问该证书的根证书来自哪个CA?

LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUhlVENDQm1HZ0F3SUJBZ0lRQy8yMENRclh0ZVpBd3dzV3lWS2FKekFOQmdrcWhraUc5dzBCQVFzRkFEQjEKTVFzd0NRWURWUVFHRXdKVlV6RVZNQk1HQTFVRUNoTU1SR2xuYVVObGNuUWdTVzVqTVJrd0Z3WURWUVFMRXhCMwpkM2N1WkdsbmFXTmxjblF1WTI5dE1UUXdNZ1lEVlFRREV5dEVhV2RwUTJWeWRDQlRTRUV5SUVWNGRHVnVaR1ZrCklGWmhiR2xrWVhScGIyNGdVMlZ5ZG1WeUlFTkJNQjRYRFRFMk1ETXhNREF3TURBd01Gb1hEVEU0TURVeE56RXkKTURBd01Gb3dnZjB4SFRBYkJnTlZCQThNRkZCeWFYWmhkR1VnVDNKbllXNXBlbUYwYVc5dU1STXdFUVlMS3dZQgpCQUdDTnp3Q0FRTVRBbFZUTVJrd0Z3WUxLd1lCQkFHQ056d0NBUUlUQ0VSbGJHRjNZWEpsTVJBd0RnWURWUVFGCkV3YzFNVFUzTlRVd01TUXdJZ1lEVlFRSkV4czRPQ0JEYjJ4cGJpQlFJRXRsYkd4NUxDQktjaUJUZEhKbFpYUXgKRGpBTUJnTlZCQkVUQlRrME1UQTNNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1EyRnNhV1p2Y201cApZVEVXTUJRR0ExVUVCeE1OVTJGdUlFWnlZVzVqYVhOamJ6RVZNQk1HQTFVRUNoTU1SMmwwU0hWaUxDQkpibU11Ck1STXdFUVlEVlFRREV3cG5hWFJvZFdJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUIKQ2dLQ0FRRUE1NGhjOHBaY2x4Z2N1cGppQS9GL09aR1J3bS9abHVjb1FHVE5US21CRWdOc3JuL214aG5nV21QdwpiQXZVYUxQLy9UNzlKYysxV1hNcHhNaXo5UEs2eVpSUkZ1SW8wZDJieDQyM05BNmhPTDJSVHRibmZzK3kwUEZTCi9ZVHBRU2VsVHVxK0Z1d3RzNXY2YUF3ZU55TWNZRDBIQnlia2tkb3NGb0RjY0JOeko5MkFjOEk1RVZEVWMzT3IKLzRqU3lad3p4dTlrZG1CbEJ6ZUhNdnNxZEg4U1g5bU5haFh0WHhScHdabkJpVWp3MzZQZ04rczlHTFdHcmFmZAowMlQwdXg5WXpkNWV6a014dWtxRUFRN0FLSUlpanZhV1BBSmJLLzUyWExoSXkydnBHTnlseW5pL0RRRDE4YkJQClQrWkcxdXYwUVFQOUx1WS9qb08rRktET1RsZXI0d0lEQVFBQm80SURlakNDQTNZd0h3WURWUjBqQkJnd0ZvQVUKUGROUXBkYWdyZTd6U21BS1pkTWgxUGo0MWc4d0hRWURWUjBPQkJZRUZJaGNTR2NaektCMldTMFJlY08rb3F5SApJaWRiTUNVR0ExVWRFUVFlTUJ5Q0NtZHBkR2gxWWk1amIyMkNEbmQzZHk1bmFYUm9kV0l1WTI5dE1BNEdBMVVkCkR3RUIvd1FFQXdJRm9EQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3ZFFZRFZSMGYKQkc0d2JEQTBvREtnTUlZdWFIUjBjRG92TDJOeWJETXVaR2xuYVdObGNuUXVZMjl0TDNOb1lUSXRaWFl0YzJWeQpkbVZ5TFdjeExtTnliREEwb0RLZ01JWXVhSFIwY0RvdkwyTnliRFF1WkdsbmFXTmxjblF1WTI5dEwzTm9ZVEl0ClpYWXRjMlZ5ZG1WeUxXY3hMbU55YkRCTEJnTlZIU0FFUkRCQ01EY0dDV0NHU0FHRy9Xd0NBVEFxTUNnR0NDc0cKQVFVRkJ3SUJGaHhvZEhSd2N6b3ZMM2QzZHk1a2FXZHBZMlZ5ZEM1amIyMHZRMUJUTUFjR0JXZUJEQUVCTUlHSQpCZ2dyQmdFRkJRY0JBUVI4TUhvd0pBWUlLd1lCQlFVSE1BR0dHR2gwZEhBNkx5OXZZM053TG1ScFoybGpaWEowCkxtTnZiVEJTQmdnckJnRUZCUWN3QW9aR2FIUjBjRG92TDJOaFkyVnlkSE11WkdsbmFXTmxjblF1WTI5dEwwUnAKWjJsRFpYSjBVMGhCTWtWNGRHVnVaR1ZrVm1Gc2FXUmhkR2x2YmxObGNuWmxja05CTG1OeWREQU1CZ05WSFJNQgpBZjhFQWpBQU1JSUJmd1lLS3dZQkJBSFdlUUlFQWdTQ0FXOEVnZ0ZyQVdrQWRnQ2t1UW1RdEJoWUZJZTdFNkxNClozQUtQRFdZQlBrYjM3ampkODBPeUEzY0VBQUFBVk5oaWVvZUFBQUVBd0JITUVVQ0lRQ0hIU0VZL1JPSzIvc08KbGpiS2FORWNLV3o2QnhISk5QT3RqU3l1Vm5TbjRRSWdKNlJxdlliU1gxdktMZVg3dnBuT2ZDQWZTMlk4bEI1UgpOTXdrNnVzMlFpQUFkZ0JvOXBqNEgyU0N2anFNN3Jrb0hVejhjVkZkWjVQVVJORUtaNnk3VDAvN3hBQUFBVk5oCmllbm5BQUFFQXdCSE1FVUNJUURacGQ1UyszdG84azdsY0RlV0JoaUpBU2lZVGsyck5BVDI2bFZhTTN4aFd3SWcKTlVxcmtJT0RacFJnK2toaHA4YWc2NUI4bXUwcDRKVUFta1JEYmlZblJ2WUFkd0JXRkFhYUw5ZkM3TlAxNGIxRQpzajdIUm5hNXZKa1JYTUR2bEpoVjFvblEzUUFBQVZOaGllcVpBQUFFQXdCSU1FWUNJUURubTNXU3RsdkU5OUdDCml6U3grVUd0R21RazJXVG9rb1BnbzFoZml2OHpJQUloQVByWWVYckJnc2VBOWpVV1dvQjRJdm1jWnRzaGpYc28KblQ4TUlHMXUxekY4TUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDTGJOdGt4dXNwcXljcThoMUVwYm1BWDB3TQo1RG9XN2hNL0ZWZHo0TEozS21mdHlrMXlkOGovUFN4UnJBUU4yTXIvZnJLZUs4TkUxY01qaTMybUpiQnFwV3RLCi8rd0MrYXZQcGxCVWJOcHpQNTNjdVRNRi9Rc3N4SXRQR05QNS9PVDlBajFCeEEvTm9mV1pLaDR1ZlY3Y3ozcFkKUkRTNEJGK0VFRlE0bDVHWSt5cDRXSkEveFN2WXNUSFdlV3hSRDEvbmw2Mi9SZDlGTjJOa2FjUlZvekN4UlZsZQpGckJIVEZ4cUlQNmtEbnhpTEVsQnJabmd0WTA3aWV0YVlaVkxRTi9FVHlxTFFmdHNmOFRlY3dUa2xianZtOE5UCkpxYmFJVmlmWXdxd05OKzRsUnhTM0Y1bE5sQS9pbDEySU9nYlJpb0xJNjJvOEcwRGFFVVFnSE5mOHZTRwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==

微信公众号:qrcode

小米安全中心:http://sec.xiaomi.com/

【注:本文为“小米安全中心”原创  作者: 那我就随便唱两句    安全脉搏整理发布】

源链接

Hacking more

...