南琴浪博客

Certificate Transparency 概述

10/28/2017

本博客 Nginx 配置(系列一) 安全篇 中,我提到了 一种名为 Certificate Transparency 的用于 HTTPS 安全的方案,本文会介绍 Certificate Transparency 相关。

什么是 Certificate Transparency

HTTPS 站点的身份认证通过证书信任链完成:浏览器从站点证书开始,递归校验父证书,直至出现信任的根证书(这个根证书列表一般内置于操作系统,Firefox 则自己维护)。然而,世界上受信任的 CA 机构数量较大(几百个),它们组成了整个站点身份认证过程中一个较大的有机可乘的攻击面。此外,由于 CA 的失误签发了非法证书,或者甚至是个别 CA 出于某种目的故意签发非法证书(我可没说是沃通 Wosign 啊,我不是我没有你别瞎说啊),都能利用自己的受信任 CA 的身份,让这种非法证书成功通过现有的证书信任链校验机制。比起纯粹的自签名证书,这种非法证书更容易获得信任且更难被发现它的非法,即便被发现了,由于现有证书链信任机制的缺陷,也较难应对和更正。

为了解决这些瓶颈,Certificate Transparency 诞生了。

Certificate Transparency(中文直译:证书透明度,官方主页RFC 6962),有时简称 CT,它是一个实验性的 IETF 开源标准和开源框架,目的是监测和审计数字证书。通过证书日志、监控和审计系统,证书透明度使网站用户和域名持有者可以识别不当或恶意签发的证书,以及识别数字证书认证机构(CA)的作为,从而完善了证书信任链机制,能够进行更严密的证书合法性验证。

Certificate Transparency 机制由三部分组成:

  • Certificate Logs
  • Certificate Monitors
  • Certificate Auditors

完整的工作机制说明,请看 官方文档

一般来说,证书所有者(或者是 CA)都可以主动向 Certificate Logs 服务器提交你的证书,所有证书记录都会接受监测和审计。Certificate Logs 服务器一般由 CA 部署(Google 也有部署,更多服务器列表请看 Known Logs)。合法证书向 CT Logs 服务器的合法提交完成后,服务器会返回 SCT(Signed Certificate Timestamp)信息。这个 SCT 信息是 CT 机制所必须的。

然而,Certificate Transparency 现在有个很大的问题是,目前浏览器对它的兼容比较少。目前 Chrome 已默认对它提供支持,Firefox 则需要修改 security.pki.certificate_transparency.mode 为 true。

CT 和 HPKP 实现的目的类似,由于 CT 的浏览器兼容性不佳,所以一般只启用 HPKP 而不启用 CT 也无大碍。关于 HPKP,可以参看 我的另一篇文章。当然,因为鄙人的强迫症,本站已启用这两样特性。

启用 CT 的三种方式

有以下三种方式启用 CT :

X.509v3 扩展

CA 先预签证书,并将预签证书提交给 CT Logs 服务器,得到 SCT 信息,然后 CA 将 SCT 信息作为预签证书的扩展再次签名,得到一个含有 SCT 扩展的证书。
这种方式的好处在于,站点证书的使用者不需要任何操作就能直接拿到带有 SCT 拓展的证书。但对于 CA 来说,需要实时提交证书拿到 SCT、先后签署两次证书,对于 CA 来说成本较高,但这个方案是以后的趋势。Digicert、Trustasia、Let’s Encrypt 已支持这种方案

Let’s Encrypt 已支持这种方案 via

We are dedicated to transparency in our operations and in the certificates we issue. We submit all certificates to Certificate Transparency logs as we issue them.

TLS 扩展

当然了,除了 CA 预提交这种情况,我们也可以自己提交证书给 CT Logs 服务器,记录 SCT 信息(如何提交证书和获取 SCT,我会在下面讲到)。然后配置 Web Server,使其能在 TLS 握手阶段的 Server Hello 响应中启用 Signed_Certificate_Timestamp 扩展,传递 SCT 信息。
这种方式对于 CA 来说是 0 成本。而对于用户,需要自己提交证书、配置 Signed_Certificate_Timestamp 扩展,成本较高。

通过 OCSP Stapling

提交证书,获取到 SCT 后,和“上面 TLS 扩展 中所讲的 Signed_Certificate_Timestamp 拓展”的传递 SCT 的方式不同,还有一种传递 SCT 的方式是,通过改造 OCSP 文件,将 SCT 信息写入到 OCSP 文件中,服务端启用 OCSP Stapling 后,就可以将 SCT 信息放入证书的 OCSP 查询结果中。关于 OCSP Stapling,可以参看 我的这篇文章

Nginx 启用 CT

TLS 扩展是适应最广泛的方案:

准备好你的中间证书和站点证书,按照站点证书在上、中间证书在下的顺序,将两个证书的内容复制粘贴到一个文件 ct.cer

获取 SCT 文件

通过 ct-submit 这个工具 ,可以很方便地将证书提交给 CT Logs 服务器,并得到 SCT 回应

# 编译这个工具,需要 go 语言支持
apt-get install -y golang
wget -O ct-submit-1.1.2.zip https://github.com/grahamedgecombe/ct-submit/archive/v1.1.2.zip
unzip ct-submit-1.1.2.zip
cd ct-submit-1.1.2
go build

# 备用下载,这是我已经编译好的
wget https://raw.githubusercontent.com/nanqinlang/nginx/master/ct-submit-1.1.2.zip && unzip ct-submit-1.1.2.zip

编译完成后,当前目录会出现 ct-submit-1.1.2 这个文件,赋予执行权限后运行它

chmod 7777 ct-submit-1.1.2
./ct-submit-1.1.2 ct.googleapis.com/icarus <ct.cer >ct.sct

以上,把证书文件 ct.cer 提交给 Google 的 CT Logs 服务器,并在当前目录生成 SCT 文件 ct.sct。更多可用的 CT Logs 服务器,我在上面已提到过,建议选择 Google 的服务器的其中一个即可。

Nginx 编译 CT 模块

要令 Nginx 支持 TLS 扩展,需要加入 nginx-ct 这个模块

wget -O nginx-ct-1.3.2.zip https://github.com/grahamedgecombe/nginx-ct/archive/v1.3.2.zip
unzip nginx-ct-1.3.2.zip
./configure --... --add-module=.../nginx-ct-1.3.2
make && make install

# 备用下载
wget https://raw.githubusercontent.com/nanqinlang/nginx/master/nginx-ct-1.3.2.zip && unzip nginx-ct-1.3.2.zip

Nginx 站点配置

接下来,要在你的 Nginx 配置文件中加入如下:

server {
# 开启 CT
ssl_ct on;
# 和 Nginx 的一般规则不同,这里不是指定文件,而是写入你的 ct.sct 所在的路径
ssl_ct_static_scts    /home/sometimesnaive.org/crt;

之后启动 Nginx。至此,Nginx 对 CT 的启用就完成了。

可以访问 ssllabs 进行 SSL 测试,其中能看到 Certificate Transparency 开启与否的报告。