网络安全篇,面对复杂多变的网络环境,我们需要掌握哪些关于网络安全的相关知识,聊一聊与网络安全相关的:HTTPS、SSL、TLS 等。
网络安全专题
- 网络安全的基石
《网络安全 — HTTPS》
《网络安全的基石(上)— 加密》
《网络安全的基石(下)— 完整性与身份认证》
《公钥信任问题 — 数字证书与 CA》
《信任始于握手 — TLS 连接过程详解》
- HTTPS 的优化
《TLS 1.3 特性解析》
《如何优化 HTTPS 连接》- 待完善
早在 2013 年,IETF(互联网工程小组) 就对 TLS 1.2 的过时设计和两次往返开销心生不满,因此开始着手准备新版本的 TLS。同年 8 月由 Eirc Rescorla 提议出新版本 TLS 的功能愿望清单。在经过一番辩论后,最终该提议内容被定义为 TLS 1.3。推动 TLS 1.3 设计的主要问题大概有:
- 减少握手延迟
- 加密更多的握手
- 提高抵御跨协议攻击的弹性
- 删除旧功能
终于在 2018 年 8 月 10 日,历经 4 年时间,TLS 1.3 最终版本发布了 — RFC-8446。新的协议使得互联网变得更快、更安全;随着 TLS 1.3 的采用率不断提高,它势必会长远影响互联网的发展;同时尽快将 TLS 1.3 平滑应用到线上环境无疑是势在必行。
不过在这之前, TLS 1.2 的应用也已经有 10 年(2008 年)的时间了,毕竟历经了种种考验,新的协议在推广和部署上必定会带来新的挑战。接下来我们就来看看新版本的 TLS 是如何做的?
“岁月不饶人”,如今互联网的发展已使 TLS 1.2 在安全、性能等方面不能满足如今的需求。
兼容性
由于 TLS 1.1/1.2 等协议已经出现了很多年,很多应用软件、中间代理(Middlebox)只认老的记录协议格式,更新改造很困难,甚至僵化。
可部署性
正式由于这些中间代理/软件(Middlebox)在新的更改中表现不佳,即使是对 TLS 1.3 协议的细微更改(例如消除冗余的 ChangeCipherSpec 消息,版本号从 0x03 升级为 0x04),也最终导致了某些设备的连接失败问题。这也是 TLS 1.3 从草稿到最终发布花费了这么长时间的重要原因之一。
为了保证这些被广泛部署的“旧设备”能够继续使用,TLS 1.3 不得不做出妥协,通过“伪装”来实现兼容:保持现有的记录格式不变,使得 TLS 1.3 看上去“像是” TLS 1.2。
扩展协议
那么,如何区分是 1.2 还是 1.3 呢?
这里用到一个新的扩展协议(Extension Protocol),它有点“补充条款”的意思,通过在记录末尾添加一系列的“扩展字段”来增加新的功能,旧版本的 TLS 不认识它可以直接忽略,这就实现了“向后兼容”。
在记录头的 Version 字段被兼容性“固定”的情况下,只要是 TLS 1.3 协议,握手的 “Hello” 消息后面就必须有 “supported_versions” 扩展,它标记了 TLS 的版本号,使用它就能区分新旧协议。
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Extension: Supported_versions (len=11)
Supported Version: TLS 1.3 (0x304)
Supported Version: TLS 1.2 (0x0303)
TLS 1.3 正是利用扩展实现了许多重要的功能,比如 “supported_groups” “key_share” “signature_algorithms” “server_name” 等。
强化安全
在经历十余年的实践中获得许多宝贵经验的 TLS 1.2 陆续发现了很多的漏洞和加密算法的弱点。因此消除潜在的危险设计来纠正以前的错误成为 TLS 1.3 的设计目标之一。所以新版本的 TLS 协议里要修补这些不安全的因素。
例如:
- 伪随机函数由 PRF 升级为 HKDF(HMAC-based Extract-and-Expand Key Derivation Function);
- 明确禁止在记录协议里使用压缩;
- 废除了 RC4、DES 对称加密算法;
- 废除了 ECB、CBC 等传统分组模式;
- 废除了 MD5、SHA1、SHA-244 摘要算法;
- 废除了 RSA、DH 密钥交换算法和许多命名曲线。
固定密钥交换
经过这样一番“减肥瘦身”之后,TLS 1.3 的密钥交换算法只有 ECDHE 和 DHE 了,关于椭圆曲线(ECC)也被“砍”到只剩 P-256 和 x25519 等 5 种。
首先来说下废除 RSA 和 DH 密钥交换算法的原因:
由于客户端默认会选择 ECDHE 而非 RSA 做密钥交换,这是因为它不具有“前向安全”(Forward Secrecy):“假设有人长期记录了加密的数据,然后在后续的某个时间段获得了服务器的 RSA 私钥,那么黑客就能够使用该私钥解密出之前所有报文的 “Pre-Master”,再计算出会话密钥,破解所有密文。这便是今日截获,明日破解”
而 ECDHE 算法在每次握手时都会生成一对临时公钥和私钥,每次通信的秘钥对都是不同的,也就是“一次一密”,即使黑客花大力气破解了这一次的会话密钥,也只是这次通信被攻击,之前的历史消息不会受到影响,仍然是安全的。
所以现在主流的服务器和客户端在握手阶段都已经不再使用 RSA,改用 ECDHE,而 TLS 1.3 在协议里明确废除了 RSA 和 DH 则在标准层面保证了“前向安全”。
暂时性 DH 成为在 TLS 1.3 中唯一的密钥交换机制。
固定密码
多年以来,密钥交换机制不是唯一引起安全漏洞的部分,对称密钥部分也有相当一部分问题。
同样,用于对称加密的算法在经过“减肥瘦身”之后也只保留了 AES、ChaCha20 ,分组模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法也只能用 SHA 256、SHA 384。
这样原来众多的加密算法、参数组合导致密码套件非常复杂,难以选择。而经过瘦身之后的 TLS 1.3 只剩下 5 个套件,使得客户端或服务端在选择密码套件时变得“更加容易”。然而更重要的是,这些算法在 TLS 长期的实践过程中先后已经被证实是构成不安全的因素,从而导致安全漏洞。
修复数字签名
经过前面的学习,相信你也知道 TLS 另一个重要部分是身份验证。在每个连接中服务都是用具有公钥的数字证书向客户端提供身份认证。在 RSA 加密模式下,服务器通过解密预主密钥并通过对话记录计算 MAC 来证明其对私钥的所有权。在 Diffie-Hellman 模式下,服务器使用数字签名来证明私钥的所有权。
在 TLS 1.2 和更早的版本中,服务器的签名仅涵盖部分握手。用于协商使用哪种对称密码的部分没有由私钥签名。这也导致许多引人注目的漏洞 FREAK,LogJam 等。而在 TLS 1.3 由于服务器对整个握手记录进行签名,因此可以避免这些情况。
提升性能
HTTPS 建立连接时除了要做 TCP 握手,还要做 TLS 握手,在 TLS 1.2 中会多花两个消息往返(2 - RTT),这可能导致几十毫秒甚至上百毫秒的延迟,在移动网络中延迟还会更严重。
1-RTT 模式
密码套件的大幅度简化,也就没有必要再像以前那样走复杂的的协商流程了。TLS 1.3 压缩了以前的 “Hello” 协商过程,删除了 “Key Exchange” 消息,把握手时间减少到了 “1-RTT”,效率提高了一倍。
下面是 TLS 1.3 握手过程的简图,注意与前面介绍的 TLS 1.2 对比区别在哪里。
除了标准的 “1-RTT” 握手,受 QUIC 协议的启发,客户端可以在其第一条消息中将加密的数据发送到服务器,与未加密的 HTTP 相比,没有额外的延迟成本。
在 TLS 1.2 中,有两种恢复连接的方法:会话 ID 和会话 Ticket,而 1.3 则将他们组合在一起形成称为 PSK(pre-shared key,预共享密钥)恢复的新模式。
握手分析
目前 Nginx 等 Web 服务器都能够很好的支持 TLS 1.3,但是要求底层的 OpenSSL 必须是 1.1.1。因此如果要部署需要先升级你的 OpenSSL 版本。
首先TCP 建立连接之后,浏览器首先还是发一个 “Client Hello”。
由于 1.3 的消息要兼容 1.2,所以开头的版本号、支持的密码套件和随机数(Client Random)结构都是一样的(这时的随机数是 32 个字节)。
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Random: cebeb6c05403654d66c23290...
Cipher Suites (18 suites)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
Cipher Suite: TLS_AES_256_GCM_SHA384 (0X1302)
Extension: supported_versions (len=9)
Supported Version: TLS 1.3 (0x0304)
Supported Version: TLS 1.2 (0x0303)
Extension: supported_groups (len=14)
Supported Groups (6 groups)
Supported Group: x25519 (0x001d)
Supported Group: secp256r1 (0x0017)
Extension: key_share (len=107)
Key Share extension
Client Key Share Length: 105
Key Share Entry: Group: x25519
Key Share Entry: Group: seco256r1
注意 “Client Hello” 里的扩展,“supported_versions” 表示这是 TLS 1.3,“supported_groups” 是支持的曲线,“key_share”是曲线对应的参数。
这有点是像是“有话尽量一口气说完”,还是按照老规矩进行“打招呼”,我这边有这些信息,考虑到版本升级,所以附带了一些信息,可能后面会用到。
服务器收到 “Client Hello” 同样返回 “Server Hello” 消息,还是要给出一个随机数(Server Random)和选定密码套件。
Handshake Protocol: Server Hello
Version: TLS 1.2 (0x0303)
Random: 12d2bce568bodedi3ee9...
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Extension: supported_versions (len=2)
Supported Version: TLS 1.3 (0X0304)
Extension: key_share (len=36)
Key Share extension
Key Share Entry: Group: 0x25519, Key Exchange length: 32
表面上看 Version 和 TLS 1.2 是一样的,重点是后面的扩展。“supported_versions” 里确认使用的是 TLS 1.3,然后在 “key_share” 扩展带上曲线和对应的公钥参数。
服务器的回应还是老套路,服务端对客户端的提供的信息作出选择,另外服务端还要再附加上几个参数,这次加密就协商定了。
可以看到相比 TLS 1.2 的握手过程,TLS 1.3 仅用两条消息就共享了 4 个信息:Client Random 和 Server Random、Client Params 和 Server Params。两边就可以各自用 DH 算出 “Pre-Master”,再用 HKDF 生成主密钥 “Master Secret”,效率比 TLS 1.2 提高了一大截。
在计算出主密钥后,服务器立刻发出 “Change Cipher Spec” 消息,比 TLS 1.2 提早进入加密通信,后面的证书等就都是加密的了,减少握手时明文信息泄露。
TLS 1.3 还多了一个 “Change Cipher Spec” 消息,服务器用私钥把前面的曲线、套件、参数等握手数据加了签名,作用和 “Finished” 消息差不多。但由于是私钥签名,所以强化了身份认证和防篡改。
两个“打招呼”消息之后,客户端验证服务器证书,再发 “Finished” 消息,就正式完成了握手,开始收发 HTTP 报文。
现在已经有很多网站都支持了 TLS 1.3,例如 GitHub:
最后
今天我们主要介绍了 TLS 1.3 的一些新特性,简单总结下来主要包含下面几点:
- 为了兼容旧的版本协议,TLS 1.3 不得不伪装成 TLS 1.2 的外表,将新特性在“扩展”中实现;
- TLS 在之前的实践应用中,陆续发现了很多安全隐患,因此消除这些危险因素成了 TLS 1.3 的设计目标之一,其中包含大幅删减了一些已经被证实存在安全隐患的加密算法,从协议层面进一步强化了安全。
- TLS 1.3 还简化了握手过程,只需要一个消息往返便可实现完全握手。
TLS 1.3 涉及的内容很多,有关它的更详细信息请去参照 RFC-8446,关于这部分大家还有哪些要分享的呢?欢迎您的留言或指正。
网络安全系列专题
扩展阅读