TLS 1.3 的 0-RTT 因重放攻击风险需协同防御:Nginx 必须限制非幂等方法、透传 Early-Data 头、按路径隔离;后端须拒写操作并校验一次性 token 等应用层防护。

TLS 1.3 的 0-RTT(Early Data)能显著降低首包延迟,但它的“快”是以牺牲部分安全属性为代价的——重放攻击风险真实存在,且 TLS 层本身不拦截、不校验、不缓存,全靠 Nginx 配置与后端协同防御。
重放攻击是怎么发生的
0-RTT 基于 PSK(预共享密钥)机制。客户端在首次完整握手后获得一张加密的会话票证(NewSessionTicket),其中包含 ticket_lifetime(如 86400 秒)、随机 nonce 和加密会话状态。后续连接时,它可在发送 ClientHello 的同时直接附带应用数据(Early Data)。
问题在于:这张票证在有效期内可被中间人截获,并原样重复发送。攻击者无需解密,只需重放一个支付请求、登录动作或订单提交,服务器若未识别并拦截,就可能重复执行。
典型后果包括:同一笔扣款多次成功、重复下单、账号被反复尝试暴力登录。这不是理论风险,已有支付平台因此出现线上资损事故。
关键限制有三点:
- 重放窗口 = 票证有效期(通常数小时)
- 前向保密失效:若服务端 PSK 密钥泄露,所有历史 0-RTT 流量可被解密
- 无双向认证:0-RTT 阶段不验证客户端身份,仅依赖首次握手建立的信任
Nginx 层必须做的三道阀门
Nginx 不是旁观者,而是第一道防线。它需在 TLS 解密后、转发请求前完成初步过滤和明确标识,不能把全部责任推给后端。
-
方法级拦截:用
limit_except严格限定仅GET/HEAD可走 0-RTT。对POST/PUT/DELETE等非幂等方法,一律拒绝并返回425 Too Early,避免敏感逻辑被绕过 -
显式传递标识:通过
proxy_set_header Early-Data $ssl_early_data;将是否为 0-RTT 请求明确告知后端。值为1表示 Early Data,0或空表示常规请求。避免后端靠 UA、路径或猜测判断 -
位置块隔离:防护逻辑必须写在
location块内(而非server或http),确保按路径精细控制。例如:location /static/可放开,location /api/v1/order必须收紧
后端必须承担的防御动作
Nginx 只负责“传话”,真正做决策的是后端。收到 Early-Data: 1 头后,必须立即执行以下判断:
- 若为
GET/HEAD且纯读取(如获取文章、图片、公开接口),可直答,无需额外校验 - 若含任何写操作意图(登录、下单、改密码、发消息),必须拒绝并返回
425 Too Early,强制客户端降级为 1-RTT 重发 - 若业务强依赖 0-RTT 写(极少数场景,如实时游戏状态同步),需叠加应用层防护:一次性 token + 时间戳窗口(≤ 5 秒)+ 请求指纹(body hash + client IP + UA 哈希),三者缺一不可
验证配置是否真生效
光配对不等于跑通。建议三步交叉验证:
- 用
openssl s_client -connect example.com:443 -tls1_3 -ign_eof抓包,确认 ClientHello 是否含early_data扩展,且 ServerHello 后是否立刻出现 Application Data - 在 Nginx access_log 中添加
$ssl_early_data变量,观察日志中是否出现1值 - 用 curl 模拟重放:保存一次 0-RTT 的 POST 请求原始包,稍后重复发送,检查后端是否返回
425而非200
















