Stay Hungry. Stay Foolish.

在过去的五年中,定义互联网协议的标准机构 IETF 一直致力于标准化其最重要的安全协议之一 —— 传输层安全层协议(TLS)的最新版本。TLS 用于保护 Web 等应用,提供加密以确保每个 HTTPS 网站和 API 的真实性和可靠性。其最新版本的 TLS 1.3(RFC 8446)于 2018 年 8 月 11 日发布。毫无疑问这会是今年乃至接下来很长一段时间互联网安全领域的一件大事。

SSL & TLS —— 过去

我们在浏览网页时可以看到链接会以 http:// 或者 https:// 打头,而其中的 S 则代表了安全,广为使用的 Chrome 浏览器在其最新的稳定版本中默认将所有 HTTP 链接标记为不安全。使用 HTTPS,浏览器和服务器之间的通信通过加密和进过身份验证的信道传输。通过 HTTPS 而不是 HTTP 提供内容可以让访问者确信他们看到的内容是由合法的内容所有者呈现的,并且通信是安全的,不易被窃听。在 HTTPS 的背后是一种称为 TLS 的协议。它源于九十年代中期在 Netscape 开发的称为安全套接层(SSL)的协议。20 世纪 90 年代末,Netscape 将 SSL 交给 IETF,IETF 将其重命名为 TLS,并从此成为该协议的管理者。至今,许多人仍将 Web 加密称为 SSL,即使绝大多数服务已切换到支持 TLS。

在 IETF 中,协议称为 RFC,目前广泛使用的 TLS 协议有三个版本:TLS 1.0、1.1 和 1.2。TLS 1.0 是 RFC 2246,TLS 1.1 是 RFC 4346,TLS 1.2 是 RFC 5246。而 TLS 1.3 发布为 RFC 8446。RFC通常按顺序发布,下图是各个协议发布的时间:

ssl_history

TLS 1.0 于 1999 年发布,作为一个近二十年前的协议。众所周知,除了支持弱密码学之外,它还容易受到诸如 BEAST 和 POODLE 之类的攻击 —— 这种密码学不能保证现代连接的安全性。

TLS 1.1 是一个被遗忘的“中间孩子”。它没有任何已知的协议漏洞,但是像其年幼的兄弟姐妹一样提供对不良密码学的支持。在大多数场景下,它被 TLS 1.2 取代,并且实际应用中很少看到使用 TLS 1.1。

虽然如今这些版本很少被客户端使用,但在互联网上,几乎死了死了 之间有很大的区别。大量网站和服务还保留了对 TLS 1.0 和 1.1 的支持。其目的,排除没有定时维护和升级系统以外,主要是为了兼容 老旧 的浏览器:Android 4.3 Safari 6 Internet Explorer 10 及其之前的所有设备。人们不得不在兼容性和安全性之间取舍,很多人选择了前者。

然而随着旧设备逐渐退出历史舞台,以及老协议的安全漏洞不断被爆出,越来越高的互联网隐私及安全需求逼迫网站和企业升级更为先进和安全的 TLS 协议。2017 年 6 月,PCI 安全标准委员会发文称将最晚于 2018 年 6 月 30 日禁用早期 SSL/TLS,并实施更安全的加密协议(TLS 1.1 或更高版本,强烈建议使用 TLS 1.2)以满足 PCI DSS(Payment Card Industry Data Security Standard,第三方支付行业数据安全标准)的要求,从而保护支付数据。

TLS 1.2 —— 现在

TLS 1.1 发布仅仅 2 年后,TLS 1.2 标准于 2008 年发布,这意味着几乎所有支持 TLS 1.1 的浏览器目前都能完美支持 TLS 1.2。10 年多来,从新兴事物到主流,超过 90% 的互联网 HTTPS 流量由 TLS 1.2 提供保障。TLS 1.2 在几乎所有主流现代浏览器上都得到了完美的支持: Chrome Desktop 30+Firefox 27+Android 5.0+Opera 17+Safari 7+Internet Explorer 11Microsoft Edge

TLS 1.2 的特性主要包括:

  • 用更安全的 SHA-256 替换 MD5-SHA-1 算法;
  • 增强服务器和客户端指定 Hash 和签名算法的能力;
  • 扩大经过身份验证的加密密码,主要用于 GCMCCM 模式的 AES 加密的支持;
  • 添加 TLS 扩展定义和 AES 密码组合;
  • 删除了对 SSL 的兼容以避免安全问题;

相比 TLS 1, TLS 1.2 引入了众多加密套件,在指定 cipher 的时候我们会发现,TLS 1.2 的加密套件会整合了多种加密技术来保障安全,我们看一个典型的加密套件 ECDHE-ECDSA-AES256-GCM-SHA384,不难发现此套件包括了 ECDHE ECDSA AES256 GCM SHA384 几部分,AES GCM加密密码 ECDHE-ECDSA密钥交换协议(其中 ECDHE 为密钥交换算法,ECDSA 为签名算法)。

于此同时 chacha20 流加密第一次被引入并得到了主流支持。它提供了更强的安全性以及更好的性能,尤其是在 32 位移动和可穿戴设备上。因此 Android 操作系统默认将 chacha20 系列(包括 chacha20-draft)加密作为优先加密套件提供。

ECC 证书(内置 ECDSA 公钥的证书)也被越来越多的采用,相比与传统的 RSA 证书 ,ECC(椭圆曲线加密)能在更短的公钥下实现更强的加密效果,同等安全条件下,ECC 证书文件体积更小,密钥交换的速度相对更快。ECC 证书也基本实现了主流浏览器的兼容性,除 Windows XP 等少数平台外都可以得到较好的支持。

然而,并不是 TLS 1.2 提供的 所有 加密套件都可以被看作 安全 的,在密钥交换协议中,DHE-RSAECDHE-RSADHE-DSSECDHE-ECDSA 这些提供前向安全性的协议相对能提供比较好的安全保障,而其他协议则不然;在密码协议3DES 已被证明是 较弱 的。

综合来说,如果你想提供比较安全的加密方式,建议采取以下配置:(以 Nginx + OpenSSL1.1.0 为例)

ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;

TLS 1.3 —— 未来

在线加密一直很重要,但从历史上看,它仅用于登录或在线交易等事项,而大多数其他数据都会未经加密直接传输。但在最近几年,一个主要趋势是,将 HTTPS 用于互联网上的 所有 流量。这能更好地保护我们的网络隐私和安全,免受窃听和注入攻击,但也伴随着新连接变慢的缺点。

要使浏览器和 Web 服务器就密钥达成一致,他们需要交换加密数据。然而,在 TLS 中称为“握手”的交换自从 TLS 于 1999 年标准化以来,基本保持不变。握手需要在发送加密数据之前在浏览器和服务器之间再进行两次往返(或者在恢复先前连接时进行一次往返)。与单纯的 HTTP 流量相比,HTTPS 的额外的 TLS 握手交互过程,数据加解密对 CPU 的消耗等,会对以性能为中心的应用产生负面影响。

IETF 对 TLS 1.2 的过时设计和两次往返的额外开销不满意,因此,2013 年 8 月,新版本 TLS 标准 —— TLS 1.3 的制定提上日程,其设计目的仍然围绕两个核心 —— 安全速度

  • 减少握手延迟
  • 加密握手信息
  • 提高跨协议攻击的弹性
  • 剔除旧功能

安全

TLS 1.3 移除并修复了旧版本协议中在今天看来不够安全之处,将密钥交换、对称加解密、压缩等环节中可能存在的安全隐患剔除,防范于未然。

  • 完全支持 PFS

TLS1.3 协议中选取的密钥交换算法均支持前向安全性。斯诺登事件之后互联网企业开始重视加密算法的前向安全性,防止私钥被破解之后历史数据也能被解密成明文。
为达到上述安全目的,TLS1.3 协议中废除了不支持前向安全性的 RSA 和静态 DH 密钥交换算法。

  • 废弃 DSA 证书

DSA 证书作为历史遗留产物,因安全性差,从未被大规模应用,故在 TLS 1.3 协议中被废弃。

  • 禁用CBC模式

针对 CBC 模式加密算法的攻击,历史上出现过两次,分别是 2011 年 BEAST 和 2013 年 Lucky 13,实践证明这种对称加密模式确实存在安全隐患。

  • 禁用RC4流加密算法

2011 年 9 月,研究人员发现了 BEAST 攻击,该攻击针对所有基于 CBC 模式的加密算法。为解决这个问题,专家建议采用非 CBC 模式且普及率较高的 RC4 算法作为替代方案,RC4 算法由此得到广泛应用。

随着 TLS 版本的演进,BEAST 攻击可通过升级到新版本解决,不必要采用 RC4 这种陈旧算法来替代。另外,2013 新发现的 TLS 攻击可以从 RC4 加密的密文中恢复出少量明文,证明了这种算法无法提供让人放心的安全等级。2015 年,IETF 在 rfc7465 中明确指出要禁用 RC4 流加密算法。

  • 禁用SHA1

早在 2005 年研究机构就发现 SHA1 存在理论上的漏洞,可能造成碰撞攻击。
2013 年开始 Microsoft、Google、Symantec 等厂商相继公布 SHA1 证书的升级计划并宣布 2017 年将开始停止信任 SHA1 证书。
2017 年初 Google 与荷兰研究机构 CWI Amsterdam 共同宣布破解 SHA1。

  • 禁用出口密码套件

出口密码套件是指上世纪 90 年代美国政府为让 NSA 能够破解所有加密的外国通讯消息,规定其出口的必须是安全性较弱的密码套件,例如私钥长度不大于 512 的 RSA 加密算法。在当时,安全等级较高的加密套件被视为战争武器禁止出口。

尽管 2000 年之后美国放宽了密码出口管制,但是由于历史遗留问题,许多实际场景中仍使用出口加密套件进行协商,导致 FREAK 和 LogJam 攻击的出现,这两种攻击通过中间人将加密套件降级成出口套件,进而将破解数据。

  • 禁用TLS压缩

由于 TLS 压缩存在安全漏洞,TLS1.3 协议删除了该特性。该漏洞表现为通过 CRIME 攻击可窃取启用数据压缩特性的 HTTPS 或 SPDY 协议传输的 Cookie。在成功解读身份验证 Cookie 后,攻击者可实行会话劫持并发动进一步攻击。

  • 加密握手消息

TLS 1.3 协议中规定在 ServerHello 消息之后的握手信息需要加密。TLS 1.2 及之前版本的协议中各种扩展信息在 ServerHello 中以明文方式发送,新版本中可在加密之后封装到EncryptedExtension 消息中,在 ServerHello 消息之后发送,提高数据安全性。

速度

  • 简化协议

在以前版本的 TLS 中,主要的协商机制是密码套件。密码套件几乎涵盖了可以就连接进行协商的所有内容:

  • 支持的证书类型
  • 用于传递密码的哈希函数(例如,SHA1,SHA256,...)
  • MAC 功能(例如,HMAC 与 SHA1,SHA256,...)
  • 密钥交换算法(例如,RSA,ECDHE,......)
  • 密码(例如,AES,RC4,......)
  • 密码模式,如果适用(例如,CBC)

每个密码套件由一个叫 IANA 组织维护的表中的代码点表示。每次引入新密码时,都需要将一组新的组合添加到列表中,这导致代码点的组合数量爆炸,它变得非常混乱。

TLS 1.3 删除了这些遗留功能,仅允许在三个正交协商之间进行组合:

  • 密码 + HKDF 哈希
  • 密钥交换
  • 签名算法

目前广为支持和实现的 TLS 1.3 默认加密套件为:

TLS_AES_128_GCM_SHA256 (0x1301)
TLS_AES_256_GCM_SHA384 (0x1302)
TLS_CHACHA20_POLY1305_SHA256 (0x1303)

实际应用中我们仅推荐使用如上加密套件,即使我们不指定加密算法,开启 TLS 1.3 后,这三种加密方式也将默认启用。目前为止,这些加密算法可以被认为是足够安全的。

与此同时,这种简化的密码套件协商机制,从根本上减少了协商参数集,从而开辟了一种新的可能性,使得 TLS 1.3 握手延迟从两次往返降至仅一次往返,由此提供性能提升。

  • 1-RTT 模式

TLS 1.3 现在具有更简单的密码协商模型和一组减少的密钥协商选项(没有 RSA,没有用户定义的 DH 参数),这意味着每个连接都将使用基于 DH 的密钥协议,并且服务器支持的参数很容易猜测(如 ECDHE 使用 X25519 或 P-256)。这种有限的选择,让客户端可以简单地在第一条消息中发送 DH 密钥共享,而不是等待服务器确认它愿意支持哪些密钥共享。这样,服务器可以学习共享密钥并提前一次往返发送加密数据。

dh1-2
dh1-3

  • 0-RTT 恢复

QUIC 协议带来了进一步优化的可能。它允许客户端将第一条消息中的加密数据发送到服务器,与未加密的 HTTP 相比,这不会产生额外的延迟成本。

在 TLS 1.2 中,有两种方法可以恢复连接,Session idSession ticket。在 TLS 1.3 中,这些被组合以形成称为 PSK(预共享密钥)恢复的新模式。在建立会话之后,客户端和服务器可以导出称为“恢复主密钥”的共享凭证。

对于恢复的连接,双方共享 PSK,因此除了提供前向加密之外,不需要额外的密钥交换。下次客户端连接到服务器时,可以从上一个会话中获取密钥,用它来加密数据并将会话凭证送至服务器。

0-RTT 是目前最先进的协议技术。有了它,加密的 HTTPS 请求变得 几乎 和未加密的 HTTP 请求一样快。但是,这种突破需要的代价是可重放性,TLS 为 0-RTT 请求提供的安全属性略弱于常规请求。如果攻击者捕获发送到服务器的 0-RTT 数据包,他们可以重放它,并且服务器 有可能 认为它有效。任何在服务器上更改状态的操作,如执行数据库事务或其他具有永久效果的操作,都应该被看作 潜在 的危险重放风险,将这些数据通过 0-RTT 传输是有风险的。

TLS 1.3 的部署和支持情况

TLS 1.3 从协议到落地离不开两个关键环节:服务端程序加密库,其中最核心的部分是加密库,最广为人知的加密库是 OpenSSL,由 OpenSSL 团队维护,除此之外还有 Google 维护的 BoringSSL 和 OpenBSD 维护的 LibreSSL,由于 LibreSSL 拒绝提供 draft tls1.3(即非正式版)的支持,我们只能期待如今正式版标准公布后 LibreSSL 能快速跟进。我们重点将讨论 OpenSSL 和 BoringSSL 的情况。

在此之前我们要讨论一下 TLS 1.3 的 N 个 draft version,在 5 年的各方争论和修正中,TLS 1.3 标准不断修改,诞生了 28 个草案版本,各个版本之间相互不兼容,最终各方认可的 final version 最终版实际上是 draft 28 version 版。

首个经过广泛测试的版本是 draft 18,2016 年 11 月,BoringSSL 实现了相关协议内容,Chrome Version 57 迅速跟进。2017 年 5 月,OpenSSL 团队发布了实现该草案的分支,并多数 CDN 厂商投入使用。

第二个,也是目前使用最为广泛的版本是 draft 23,2018 年初,BoringSSL 的实现完成并且在 Chrome 65 至今版本中使用,Firefox 60 将 TLS 1.3 draft 23 默认开启。OpenSSL 于 2018 年 2 月发布的 OpenSSL 1.1.1-pre2 版本中发布了不完全的实现版本(此版本存在 TLS 握手的问题,然而 OpenSSL 在 1.1.1pre2 后删去了 draft 23 转而实现之后的草案版本)。

最终版本的实现也由 BoringSSL 率先提供(2018 年 4 月),而在 8 月 14 日,实现了最终的 TLS 1.3 RFC 标准 ,OpenSSL 1.1.1 即最终实现版本还未发出,但是从 OpenSSL 1.1.1-pre7 已经提供了较为完善的支持。预计 Chrome 和 Firefox 都会很快将这个最终版本添加为默认支持。

为你的网站开启 TLS 1.3

  • Nginx + BoringSSL 为例,操作系统 Ubuntu 18.04
  • 详细的编译教程可以参考 Google 用什么 Nginx 一文
  • 编译 BoringSSL
apt install zip clang libpcre3-dev zlib1g-dev unzip golang cmake build-essential autoconf automake libjemalloc-dev libssl-dev git -y
cd /usr/local/src && git clone https://github.com/S8Cloud/sslpatch.git
git clone -b master https://github.com/google/boringssl.git && cd boringssl
patch -p1 <../sslpatch/BoringSSL-enable-AES_CBC.patch
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true -DOPENSSL_SMALL=1 .. && make -j2
cd .. && mkdir -p .openssl/lib && cd .openssl && ln -s ../include .
cd .. && cp build/crypto/libcrypto.* build/ssl/libssl.* .openssl/lib && cd ..
  • 编译 Nginx
git clone https://github.com/nginx/nginx.git && cd nginx && cp auto/configure .
./configure --with-cc-opt='-m64 -march=native -DTCP_FASTOPEN=23 -g -O3 -fPIE -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wno-unused-parameter -fno-strict-aliasing -fPIC -D_FORTIFY_SOURCE=2 -gsplit-dwarf' --with-ld-opt='-lrt -ljemalloc -Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now -fPIC' \
--sbin-path=/usr/sbin/nginx --user=www-data --group=www-data \
--with-compat --with-file-aio --with-threads --with-poll_module --with-select_module --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_sub_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_degradation_module --with-http_secure_link_module --with-http_slice_module --with-http_stub_status_module --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-libatomic --with-zlib --with-pcre --with-pcre-jit \
--with-openssl=../boringssl

touch ../boringssl/.openssl/include/openssl/ssl.h
make -j2 && make install
  • 开启 TLS1.3

目前 Nginx 无法指定 TLS 1.3 的 ciphers,保留默认配置

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:[ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA';
ssl_ecdh_curve X25519:P-256;
# 0-RTT 选项,默认关闭
ssl_early_data on;
ssl_prefer_server_ciphers on;

本文参考

A Detailed Look at RFC 8446
Transport Layer Security
白山 HTTPS 功能升级——TLSv1.3 协议