协议特性
TLS 1.3 详解:相比 1.2 改了什么、怎么开启
TLS 1.3 是传输层安全协议的当前版本,最初于 2018 年 8 月以 RFC 8446 发布,现行规范是 2026 年 7 月的 RFC 9846。和 TLS 1.2 相比,新连接只要 1 个往返,握手大部分内容(包括服务器证书)都被加密,过去十年引发过攻击的老算法全部删除。截至 2026 年 11 月,主流浏览器、操作系统和 TLS 库都已支持,服务器应开启 TLS 1.3,同时保留加固过的 TLS 1.2 兼容老客户端。
和 TLS 1.2 相比改了什么
TLS 1.3 是一次重新设计,删掉的东西比新增的多。
| 方面 | TLS 1.2(RFC 5246,现已被 RFC 9846 取代) | TLS 1.3(RFC 9846,最初为 RFC 8446) |
|---|---|---|
| 完整握手 | 2 个往返 | 1 个往返 |
| 会话恢复 | Session ID 或票据,1 个往返 | PSK 票据,可选 0-RTT |
| 密钥交换 | 静态 RSA、DHE、ECDHE | 只有临时 (EC)DHE 或混合算法,或 PSK + (EC)DHE |
| 前向保密 | 只有 (EC)DHE 套件才有 | 完整握手始终具备 |
| 加密算法 | CBC、RC4、3DES、GCM、ChaCha20…… | 只有 AEAD:AES-GCM、ChaCha20-Poly1305、AES-CCM |
| 密码套件 | 数百种组合 | 5 个 |
| DH 参数 | 服务器可以发任意参数 | 只能用命名组(X25519、P-256、RFC 7919 的 FFDHE……) |
| 握手签名 | 允许 RSA PKCS#1 v1.5、SHA-1、MD5 | RSA-PSS、ECDSA、EdDSA;PKCS#1 v1.5 仅限老式客户端密钥(RFC 9963);不再用 SHA-1、MD5 |
| 证书 | 明文发送 | 加密发送 |
| 压缩 | 支持(CRIME,2012) | 删除 |
| 重协商 | 支持 | 删除,由 KeyUpdate 和握手后认证代替 |
| ChangeCipherSpec | 真实的协议消息 | 删除(为兼容中间设备保留一个假消息) |
| 密钥派生 | 自定义 PRF | HKDF + 结构化密钥调度 |
删掉的东西都不是摆设:静态 RSA 密钥交换没有前向保密,还招来了 ROBOT(2017)这类 Bleichenbacher 式攻击;CBC 填充招来了 Lucky Thirteen;压缩招来了 CRIME;重协商在 2009 年需要紧急打补丁;任意 DH 参数招来了 Logjam。每一项删除关掉的是一整类漏洞。
2026 年 7 月,IETF 把 TLS 1.3 重新发布为 RFC 9846。这是一次向后兼容的小修订,版本号不变:澄清了含糊的条文,禁止在不同连接之间复用 key share,禁止协商 TLS 1.0 和 1.1,限制 KeyUpdate 消息的数量,新增 general_error 告警,并把 "master" secret 的叫法改为 "main"。它同时取代了 RFC 5246,TLS 1.2 的实现要求也写进了同一份文档。
更快:1-RTT 与 0-RTT
TLS 1.3 靠“客户端先猜”省掉一个往返。ClientHello 里已经带了客户端估计服务器会接受的 key share,服务器第一次回复就能算出密钥,并把证书、签名和 Finished 一起发回来。客户端发出自己的 Finished 后马上就能发 HTTP 请求,前后只用 1 个往返。
TLS 1.2 完整握手 TLS 1.3 完整握手
C: ClientHello ---> C: ClientHello + key_share --->
S: ServerHello, Certificate, S: ServerHello + key_share,
ServerKeyExchange, {EncryptedExtensions,
ServerHelloDone <--- Certificate, CertificateVerify,
C: ClientKeyExchange, Finished} <---
ChangeCipherSpec, Finished ---> C: {Finished} + [GET /] --->
S: ChangeCipherSpec, Finished <--- S: [200 OK] <---
C: [GET /] --->
S: [200 OK] <---
= 发请求前 2 个往返 = 发请求前 1 个往返
猜错了服务器就回 HelloRetryRequest,握手变成 2 个往返,也不比 TLS 1.2 差。每条消息的细节见 TLS 握手详解。
恢复会话时还能更进一步用 0-RTT:客户端用上次会话票据里的预共享密钥,在第一个包里就带上应用数据。代价在安全上:早期数据无法防重放,因为服务器没法确认它是新鲜的;它相对票据密钥也没有前向保密。TLS 1.3 规范要求应用显式开启,RFC 8470 规定了 HTTP 的处理方式:Early-Data: 1 请求头和 425 Too Early 状态码。大多数服务器默认关闭 0-RTT,Nginx 的 ssl_early_data 默认就是 off。见 0-RTT。
更隐私:握手被加密
TLS 1.3 中 ServerHello 之后的一切都加密,包括服务器证书链。在 TLS 1.2 下,被动观察者能直接从线路上读到证书,也就知道了 SAN 里的所有域名;TLS 1.3 下它只能看到 ClientHello 和 ServerHello。
剩下的泄露点是 ClientHello 里的 SNI:服务器得先知道域名才能选证书。加密 ClientHello(ECH,2026 年 3 月标准化为 RFC 9849)补上了这个缺口:客户端从 HTTPS DNS 记录里取得服务器的 ECH 公钥,把真正的 ClientHello 加密后包在一个只写公共前端域名的外层 ClientHello 里。ECH 只能在 TLS 1.3 上用,这也是升级的理由之一。
降级保护
如果攻击者能让双方都以为对方只支持 TLS 1.2,就可能把连接引向更弱的算法。TLS 1.3 有两层防护。
第一层,真实版本在 supported_versions 扩展里协商,整个握手又被 Finished 和服务器签名保护。第二层,支持 TLS 1.3 的服务器一旦协商出更低版本,会在 ServerHello random 的最后 8 字节写入标记:
支持 1.3 的服务器协商到 TLS 1.2: 44 4F 57 4E 47 52 44 01 ("DOWNGRD" 01)
协商到 TLS 1.1 或更低: 44 4F 57 4E 47 52 44 00 ("DOWNGRD" 00)
server random 在服务器签名范围内,攻击者删不掉这个标记,支持 TLS 1.3 的客户端看到它就中止连接。它取代了早先的 TLS_FALLBACK_SCSV(RFC 7507),后者只能防住浏览器自己降版本重试那一种情况。
中间设备兼容模式
TLS 1.3 握手被故意伪装成 TLS 1.2 的会话恢复。标准制定期间的实测发现,不少防火墙、代理和流量审计设备会丢弃“看起来不像 TLS 1.2”的连接,所以最终设计里加了几层伪装:
ClientHello.legacy_version固定写0x0303(TLS 1.2),真实版本列表放在supported_versions;- 记录层头部也写
0x0303,加密记录的外层类型一律是application_data; - 客户端发送随机的 32 字节
legacy_session_id,服务器原样回显; - 双方各发一条没有意义的
ChangeCipherSpec。
所以抓 TLS 1.3 的包会看到“TLS 1.2”的记录头和 ChangeCipherSpec。openssl s_client -msg 输出里出现 <<< TLS 1.2, RecordHeader 后面跟着 <<< TLS 1.3, ChangeCipherSpec 是正常的。国内企业网常见的上网行为管理、SSL 解密网关如果版本太老,正是这类兼容问题的高发区。
后量子密钥交换只在 TLS 1.3 上
新的密钥交换方式以命名组的形式加入 TLS 1.3,不需要改密码套件。最重要的是 X25519MLKEM768(编号 0x11EC),由 X25519 和 NIST 标准化的 ML-KEM-768 组合而成,2026 年 8 月与 SecP256r1MLKEM768、SecP384r1MLKEM1024 一起在 RFC 10024 中标准化。Chrome 131 于 2024 年 11 月默认启用,2025 年 4 月发布的 OpenSSL 3.5 也把它放进了默认 key share。这些混合算法只为 TLS 1.3 定义,停留在 TLS 1.2 的服务器根本无法提供抗量子的密钥交换。详见后量子 TLS。
哪些东西不变
开启 TLS 1.3 不需要换证书、换私钥或改证书链。同一张 RSA 或 ECDSA 证书可以同时服务 TLS 1.2 和 TLS 1.3 客户端;OCSP 装订和 SCT 照常工作(改为挂在 Certificate 条目的扩展里);HSTS 等 HTTP 层设置不受影响;ALPN 照样协商 h2 或 http/1.1,HTTP/2 无需改动。
RSA 密钥在 TLS 1.3 下有一条限制:TLS 1.3 服务器的 RSA 密钥必须用 RSA-PSS 签握手,哪怕证书本身是 PKCS#1 v1.5 签发的;最初的 RFC 8446 对客户端也是同样要求。软件密钥对此无感,但一些老的硬件加密模块、智能卡、USB Key 和 TPM 里的客户端密钥只能做 PKCS#1 v1.5 签名。RFC 9963(2026 年 4 月)为这类客户端密钥新增了 rsa_pkcs1_*_legacy 编号,服务器可以在 CertificateRequest 里提供。客户端或服务器任一方没实现 RFC 9963 时,这类密钥仍是在 TLS 1.2 下正常、到了 TLS 1.3 就失败,典型表现是升级后基于客户端证书的登录(比如网银、政务系统常见的 U 盾类方案)突然不能用。ECDSA 密钥没有这个问题。
会话恢复的形态也变了:TLS 1.2 的 Session ID 和票据换成了 TLS 1.3 的 PSK 票据。负载均衡后的多台服务器仍要同步票据密钥,而旧的 Session ID 缓存对 TLS 1.3 客户端不再起作用。
谁支持 TLS 1.3
当前版本的软件全都支持,问题出在老环境上。
| 软件 | 首个支持正式版 TLS 1.3 的版本 |
|---|---|
| OpenSSL | 1.1.1(2018 年 9 月) |
| Chrome | 70 |
| Firefox | 63 |
| Safari | 12.1 |
| Android | 10 |
| Java | 11(并向后移植到 8u261) |
| Windows(Schannel) | Windows 11、Windows Server 2022 |
| Apache httpd | 2.4.36,需 OpenSSL 1.1.1 |
| Nginx | 1.13.0,需 OpenSSL 1.1.1 |
薄弱环节是 TLS 库早于 2018 年的服务器。CentOS 7、RHEL 7 自带 OpenSSL 1.0.2,国内不少老服务器还跑着它;Windows Server 2019 及更早版本的 Schannel 没有 TLS 1.3,IIS 站点要升级系统。Java、Go 这类自带 TLS 实现的软件不受系统 OpenSSL 影响。
怎么开启 TLS 1.3
在较新的环境里,开启 TLS 1.3 只是在协议列表里加一个词。
# Nginx(需用 OpenSSL 1.1.1 或更高版本编译)
ssl_protocols TLSv1.2 TLSv1.3;
# Apache httpd 2.4.36+,OpenSSL 1.1.1+
SSLProtocol -all +TLSv1.2 +TLSv1.3
先确认库版本:nginx -V 2>&1 | grep -i openssl 能看到 Nginx 编译时用的 OpenSSL;Apache 可以用 httpd -V 和 apachectl -M。然后从外部验证:
openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief </dev/null
curl -sv --tlsv1.3 -o /dev/null https://example.com/ 2>&1 | grep -E 'SSL connection|TLSv1.3'
服务器不支持 TLS 1.3 时,s_client 会因协议版本告警而失败。SSL 检测工具会列出支持的版本,套件与协议命令手册里有逐个版本测试的命令。
国内常见环境:
- 宝塔面板:网站设置的 SSL 页面通常可以勾选 TLS 版本,也可以直接改配置文件里的
ssl_protocols。改完用上面的命令从外网验证一次。 - CentOS 7 自编译 Nginx:系统 OpenSSL 1.0.2 不能用,编译时用
--with-openssl=指向新版 OpenSSL 源码,或者迁移到更新的发行版。 - 阿里云、腾讯云负载均衡与 CDN:在 HTTPS 监听或域名配置里选择包含 TLS 1.3 的安全策略。这时源站配置不影响访客,访客只和边缘节点握手。
常见部署问题
大多数 TLS 1.3 问题出在 Web 服务器外围,而不是协议本身。
- 对外的不是你的服务器:CDN、云负载均衡或 WAF 终止 TLS 时,协议由它们的策略决定,源站 Nginx 怎么配访客都看不到。
- 只开 TLS 1.3 会挡掉老客户端:Mozilla modern 配置列出的最老客户端是 Firefox 63、Chrome 70、Safari 12.1、Android 10、Java 11。更老的安卓手机、旧版 Java 程序、嵌入式设备会报 ERR_SSL_VERSION_OR_CIPHER_MISMATCH 或笼统的协议错误。
- 按路径要客户端证书:TLS 1.2 时代常用重协商只对某些路径要求客户端证书。TLS 1.3 没有重协商,HTTP/2 下又不允许握手后认证(RFC 8740),也不是所有客户端都实现了它。应改为整个域名统一要求客户端证书,见双向 TLS。
- 0-RTT 重放:打开早期数据却不在应用层做检查,重放的请求就会被执行。
- 流量审计设备:老旧的 SSL 解密代理可能处理不了 TLS 1.3 或更大的后量子 ClientHello,真正的解决办法是升级设备。
TLS 1.0 和 1.1 应该早就关掉了:RFC 8996 在 2021 年 3 月正式废弃了这两个版本,RFC 9846 进一步禁止协商它们,主流浏览器在 2020 年前后已经移除。截至 2026 年 11 月,TLS 1.2 + 1.3 配合密码套件一文里的套件列表,是稳妥的默认配置。
常见问题
有了 TLS 1.3,可以关掉 TLS 1.2 吗?
大多数公网站点还不行。截至 2026 年 11 月,Mozilla 的 intermediate 配置仍保留 TLS 1.2(只用 ECDHE + AEAD 套件),照顾老手机、嵌入式设备和部分企业软件。客户端完全可控的 API 和内部服务可以只开 TLS 1.3。
0-RTT 能放心打开吗?
只适合可以安全重放的请求。攻击者截获 0-RTT 数据后可以重发,像支付这样的非幂等 POST 可能被执行两次。Nginx 的 ssl_early_data 默认关闭;如果打开,要把 $ssl_early_data 变量传给后端,对不安全的请求返回 425 Too Early(RFC 8470)。
配置里写了 TLSv1.3,为什么检测不到?
常见原因有三个:Web 服务器链接的是不支持 TLS 1.3 的旧版 OpenSSL(如 1.0.2);对外提供服务的其实是前面的 CDN 或云负载均衡,它们有自己的 TLS 策略;或者别的 server 块里的 ssl_protocols 覆盖了你的设置。用 nginx -V 看 OpenSSL 版本,再用 openssl s_client -tls1_3 从外部测试。
TLS 1.3 能隐藏访问的域名吗?
证书被加密了,但 ClientHello 的 SNI 扩展里域名仍是明文。基于 TLS 1.3 的 ECH(加密 ClientHello,RFC 9849)可以把 SNI 也加密,前提是站点在 DNS 里发布了 ECH 配置、客户端也支持。
资料来源
- RFC 9846: The Transport Layer Security (TLS) Protocol Version 1.3(2026 年 7 月)
- RFC 8446: TLS 1.3 最初版本(已被 RFC 9846 取代)
- RFC 8996: Deprecating TLS 1.0 and TLS 1.1
- RFC 8470: Using Early Data in HTTP
- RFC 10024: Post-Quantum Traditional (PQ/T) Hybrid Key Agreement Mechanisms for TLS 1.3
- RFC 9849: TLS Encrypted Client Hello
- nginx: Module ngx_http_ssl_module
- Mozilla wiki: Security/Server Side TLS