TLS 1.3 的 0-RTT 虽降低延迟但存在重放风险,Nginx 需通过方法级拦截、显式传递 Early-Data 标识、location 块隔离三道阀门防控,后端须根据该标识拒绝非幂等操作或叠加应用层防护。

TLS 1.3 的 0-RTT(Early Data)确实能大幅降低首包延迟,但它的“快”是有代价的:重放攻击风险真实存在,且无法由 TLS 层自动拦截。Nginx 不校验、不丢弃、不缓存 0-RTT 请求,只负责透传——安全责任完全落在配置策略与后端逻辑上。
重放攻击是怎么发生的?
0-RTT 基于 PSK(预共享密钥)机制,客户端在首次完整握手后获得一张“会话票证”。后续连接时,它可在发送 ClientHello 的同时直接附带应用数据(即 Early Data)。问题在于:这张票证在有效期内(通常数小时)可被网络中间人截获并无限次重发。
攻击者无需破解加密,只需原样重放一个支付请求、登录请求或订单提交,服务器若未识别并拦截,就可能重复执行——某支付平台曾因此出现同一笔扣款成功多次的事故。
关键限制有三点:
- 重放窗口 = 票证有效期(
ticket_lifetime) - 前向保密失效:若服务端 PSK 密钥泄露,所有历史 0-RTT 流量可解密
- 无双向认证:0-RTT 阶段不验证客户端身份,仅依赖首次握手建立的信任
Nginx 层必须做的三道安全阀门
不能把防重放全押给后端。Nginx 是第一道防线,应在 TLS 解密后、请求转发前完成初步过滤和标记。
-
方法级拦截:用
limit_except严格限定仅 GET/HEAD 可走 0-RTT。非幂等操作(POST/PUT/DELETE)一律拒绝,避免敏感逻辑被绕过 -
显式传递标识:通过
proxy_set_header Early-Data $ssl_early_data;将是否为 0-RTT 请求明确告知后端,避免后端靠猜或漏判 -
位置块隔离:防护逻辑必须写在
location块内(而非 server 或 http),确保按路径精细控制。例如静态资源目录可放开,API 路径必须收紧
后端必须承担的防御动作
Nginx 只是“传话人”,真正做决策的是后端。收到 Early-Data: 1 头后,必须立即执行以下判断:
- 若为 GET/HEAD 且纯读取(如获取文章、图片、公开接口),可直答,无需额外校验
- 若含任何写操作意图(登录、下单、改密码、发消息),必须拒绝并返回
425 Too Early,强制客户端降级为 1-RTT 重发 - 若业务强依赖 0-RTT 写(极少数场景),需叠加应用层防护:一次性 token + 时间戳窗口(≤ 5 秒)+ 请求指纹(如 body hash + client IP + UA 哈希),三者缺一不可
验证配置是否真生效
光配对不等于跑通。建议三步交叉验证:
- 抓包看
ClientHello是否含early_data扩展;再确认 ServerHello 后是否立刻出现Application Data(无等待客户端 Finished) - 浏览器访问后,在 DevTools → Security 标签页确认协议为 TLS 1.3;刷新一次,观察 Network 中 TTFB 是否比首次连接明显缩短(尤其在高延迟网络下)
- 用 curl 模拟重放:
curl -k -X POST -H "Early-Data: 1" https://yoursite.com/api/order,检查是否被 Nginx 拦截(返回 405 或 425)而非被后端接收











