简单认识 Certificate Transparency
根据维基百科的解释,Certificate Transparency (CT) 目前是一项实验性的 IETF 开放标准以及开放源代码的框架,用于监控以及审计数字证书。通过证书日志、监控以及审计系统,将允许网站用户以及域名所有者来判断辨别错误或者恶意签发的证书,并且可以找出那些流氓的 CA 。 翻译自维基百科:https://en.wikipedia.org/wiki/Certificate_Transparency
更多参考:What is Certificate Transparency?
Certificate Transparency 的用途
Certificate Transparency 看上去很高端是不是,我相信你也没搞清楚这个证书透明度是做什么的,通过下面这个真实的例子我来给你简单讲述一下。
在我们访问一个 https 网站的时候,这些网站都会有一个 SSL 证书,来证明这个网站就是你要访问的这个网站,并且证明网站的数据在传输过程中并没有遭到篡改。相信大家都有过被运营商进行流量挟持,被运营商在你所想要访问的页面上插广告的经历,这是因为,http 协议并不是加密的,数据在传输过程中遭到了中间设备的挟持。而 https 可以解决这个问题,因为数据的传输过程是加密的,如果运营商想要进行挟持的话,他必须有一张受浏览器信任的证书,但是这个证书他们基本上是无法获得的。
对于我们普通站长所得到的证书来说,他是由 CA (证书颁发机构) 一层一层发下来的。
以本站为例,本站的证书是由 COMODO ECC Certification Authority
签发给 COMODO ECC Domain Validation Secure Server CA
最后再签发给我 imlonghao.com
而 COMODO ECC Certification Authority
,则是由 AddTrust External CA Root
签发,AddTrust External CA Root
是默认安装在我们的电脑或者浏览器之中,起到根证书的作用。
由我们电脑中位于信任区的根证书签发下来的证书,包括这些证书再签发下去的证书,在没有专门被 revoke
的情况下,都是会被我们的系统所信任的。
我们来看这样一条新闻:Improved Digital Certificate Security
这条新闻就告诉了我们 Certificate Transparency 的功用
我们先来看一看谷歌的证书链
由 GeoTrust Global CA
签发给 Google Internet Authority G2
然后给签发下去到具体的域名
而上述新闻当中,Symantec’s Thawte-branded CA
就错误地为 google.com
以及 www.google.com
签发了证书,这是相当危险的,万一有人拿到了私钥,就可以到处冒充谷歌了。
根据后面 Symantec 方面所作出的解释,证书是在内部测试流程中签发的,并且证书的有效期只有一天,而且证书并没有流出到公众,也没有对用户造成影响。
那么,谷歌是怎么知道 Symantec 签发了一个他们的证书呢?难道是 Symantec 公司有内鬼呢?
根据谷歌的通告,他们是通过 Certificate Transparency 的日志发现了这个证书。
引用 Jerry Qu 对 Certificate Transparency 的解释
Certificate Transparency 的目标是提供一个开放的审计和监控系统,可以让任何域名所有者或者 CA 确定证书是否被错误签发或者被恶意使用,从而提高 HTTPS 网站的安全性。
如何启用 Certificate Transparency
- X509v3 Certificate Extension
- TLS Extension
- OCSP Stapling
关于三种方式的差异,请参考文末的参考资料。
Nginx 通过 TLS 拓展启用 Certificate Transparency
通过 TLS Extension
启用 Certificate Transparency
是一种比较通用的方式,无论你用的是哪一个 CA 签发下来的证书,都可以通过这种方式来启用 Certificate Transparency
提交证书并获取 SCT 文件
正如我们前面所说的,我们需要提交自己的证书到 CT Log 服务器中。这里我们用到了一个 GO 语言写的工具:ct-submit
通过下面的命令即可安装编译
apt-get install golang git
git clone https://github.com/grahamedgecombe/ct-submit
cd ct-submit
go build
提交的命令大概如下
./ct-submit [log服务器] < [你的证书位置] > [sct文件保存位置]
需要讲一下的是,上面 [你的证书位置]
所指的是完整的证书链
例子
./ct-submit ct.googleapis.com/aviator < /var/ssl/chained.pem > /var/scts/aviator.sct
Log 服务器列表可以在这里找到:Known Logs - Certificate Transparency
部分证书不能提交到部分的 Log 服务器中,谷歌官方的几个是可以的。
编译 Nginx 的 nginx-ct 模块
为了让我们的 Nginx 支持 Certificate Transparency ,我们就必须编译 nginx-ct 这个模块了
nginx-ct 对 OpenSSL 的版本有要求,要求必须是
- OpenSSL 1.0.2 or above.
- BoringSSL 4fac72e or above.
我们使用打过 CloudFlare 补丁的 OpenSSL 1.0.2 stable
安装一些编译所需要的库
apt-get install libpcre3 libpcre3-dev openssl libssl-dev unzip build-essential zlib1g-dev
准备好打过 CloudFlare 补丁的 OpenSSL 1.0.2 stable
git clone https://github.com/cloudflare/sslconfig
wget -O openssl.zip -c https://github.com/openssl/openssl/archive/OpenSSL_1_0_2-stable.zip
unzip openssl.zip
mv openssl-OpenSSL_1_0_2-stable/ openssl
cd openssl && patch -p1 < ../sslconfig/patches/openssl__chacha20_poly1305_cf.patch && cd ..
准备编译 Nginx 以及 nginx-ct,截至目前 Nginx 1.9.11 并不是稳定版,所以请自行选择您的 Nginx 版本
wget -c http://nginx.org/download/nginx-1.9.11.tar.gz
tar zxf nginx-1.9.11.tar.gz
wget -O nginx-ct.zip -c https://github.com/grahamedgecombe/nginx-ct/archive/v1.0.0.zip
unzip nginx-ct.zip
cd nginx-1.9.11/
Nginx 编译参数,注意我们要加上我们的 nginx-ct 模块,以及指定我们所使用的 OpenSSL,编译参数我所使用的是官方 mainline 源安装中的 nginx 通过 nginx -V
所得到的参数,并未作过多修改。
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=%{_libdir}/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-threads --with-stream --with-stream_ssl_module --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-http_v2_module --with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,--as-needed' --with-ipv6 --add-module=../nginx-ct-1.0.0 --with-openssl=../openssl
开始编译安装
make && make install
贴一份我所使用的 /etc/init.d/nginx
文件
#!/bin/sh
### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $network $remote_fs $local_fs
# Required-Stop: $network $remote_fs $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Stop/start nginx
### END INIT INFO
# Author: Sergey Budnevitch <sb@nginx.com>
PATH=/sbin:/usr/sbin:/bin:/usr/bin
if [ -L $0 ]; then
SCRIPTNAME=`/bin/readlink -f $0`
else
SCRIPTNAME=$0
fi
sysconfig=`/usr/bin/basename $SCRIPTNAME`
[ -r /etc/default/$sysconfig ] && . /etc/default/$sysconfig
DESC=${DESC-nginx}
NAME=${NAME-nginx}
CONFFILE=${CONFFILE-/etc/nginx/nginx.conf}
DAEMON=${DAEMON-/usr/sbin/nginx}
PIDFILE=${PIDFILE-/var/run/nginx.pid}
SLEEPSEC=1
UPGRADEWAITLOOPS=5
[ -x $DAEMON ] || exit 0
DAEMON_ARGS="-c $CONFFILE $DAEMON_ARGS"
. /lib/init/vars.sh
. /lib/lsb/init-functions
do_start()
{
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS
RETVAL="$?"
return "$RETVAL"
}
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --oknodo --retry=TERM/30/KILL/5 --pidfile $PIDFILE
RETVAL="$?"
rm -f $PIDFILE
return "$RETVAL"
}
do_reload() {
#
start-stop-daemon --stop --signal HUP --quiet --pidfile $PIDFILE
RETVAL="$?"
return "$RETVAL"
}
do_configtest() {
if [ "$#" -ne 0 ]; then
case "$1" in
-q)
FLAG=$1
;;
*)
;;
esac
shift
fi
$DAEMON -t $FLAG -c $CONFFILE
RETVAL="$?"
return $RETVAL
}
do_upgrade() {
OLDBINPIDFILE=$PIDFILE.oldbin
do_configtest -q || return 6
start-stop-daemon --stop --signal USR2 --quiet --pidfile $PIDFILE
RETVAL="$?"
for i in `/usr/bin/seq $UPGRADEWAITLOOPS`; do
sleep $SLEEPSEC
if [ -f $OLDBINPIDFILE -a -f $PIDFILE ]; then
start-stop-daemon --stop --signal QUIT --quiet --pidfile $OLDBINPIDFILE
RETVAL="$?"
return
fi
done
echo $"Upgrade failed!"
RETVAL=1
return $RETVAL
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc -p "$PIDFILE" "$DAEMON" "$NAME" && exit 0 || exit $?
;;
configtest)
do_configtest
;;
upgrade)
do_upgrade
;;
reload|force-reload)
log_daemon_msg "Reloading $DESC" "$NAME"
do_reload
log_end_msg $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_configtest -q || exit $RETVAL
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload|upgrade|configtest}" >&2
exit 3
;;
esac
exit $RETVAL
杂项(依照的是我的 nginx.conf
)
chmod +x /etc/init.d/nginx
mkdir -p /var/cache/nginx/client_temp
mkdir /etc/nginx/conf.d
/usr/sbin/update-rc.d -f nginx defaults
修改 nginx 配置启用 ct
ssl_ct_static_scts
指向的是存放我们 sct 文件的文件夹,因为 sct 文件可能有很多个…
将下列两行加上到你站点的配置文件即可,具体参数具体而定。
ssl_ct on;
ssl_ct_static_scts /var/scts;
最后重启一下你的 Nginx ,应该就是大功告成的了。