必须交叉验证三件事:抓包确认ClientHello含early_data扩展且ServerHello后直出Application Data;openssl ciphers -v 'TLSv1.3'输出至少三条原生套件;nginx -V和openssl version确认版本达标且模块启用。

排查 Nginx 环境下的 0-RTT 重放攻击风险,核心是确认“是否真启用了 0-RTT”以及“启用后是否做了必要拦截”,而不是只看配置有没有 ssl_early_data on。很多部署看似开了,实则静默降级或完全暴露风险。
验证 0-RTT 是否真实生效
光写配置不等于跑通。必须交叉验证三件事:
- 用 Wireshark 或
tcpdump抓包,观察 ClientHello 是否含early_data扩展;再确认 ServerHello 后是否立刻出现 Application Data(中间无等待) - 执行
openssl ciphers -v 'TLSv1.3',输出至少三条原生套件(如TLS_AES_256_GCM_SHA384),否则协商会退到 TLS 1.2 - 检查运行时环境:
nginx -V确认版本 ≥ 1.15.0 且编译含--with-http_ssl_module;openssl version确保 ≥ 1.1.1;证书私钥权限为600,SELinux/AppArmor 未拦截读取
定位高危路径与方法的配置漏洞
0-RTT 天然可重放,最危险的是允许 POST/PUT/DELETE 走该路径。重点检查以下配置是否存在缺失:
- 所有带副作用的接口(如
/login、/api/v1/order、/user/profile)是否单独配了location块,并在其中使用limit_except GET HEAD { deny all; } - 是否遗漏
proxy_set_header Early-Data $ssl_early_data;—— 若没透传,后端无法识别 0-RTT 请求,等同于放弃防御主动权 - 是否混用了 TLS 1.2 套件(如
ECDHE-RSA-AES256-SHA)在ssl_ciphers中?这会导致 TLS 1.3 协商失败,0-RTT 彻底失效
检查会话复用与票据机制是否就绪
没有 PSK(预共享密钥),0-RTT 就没有凭据基础。以下配置缺一不可:
-
ssl_session_tickets on;必须开启,且ssl_session_timeout建议设为1d或更长(短于 ticket_lifetime 会导致客户端拿不到票证) -
ssl_session_ticket_key /etc/nginx/ticket.key;密钥需用openssl rand 80 > ticket.key生成,并设权限400 -
ssl_session_cache shared:SSL:10m;和ssl_protocols TLSv1.2 TLSv1.3;必须同时存在 —— 只开 TLSv1.3 会断掉旧客户端,但不含它又无法触发 0-RTT
后端协同防护是否被忽略
Nginx 不校验、不拦截、不防重放,只负责传递信号。若后端没响应 Early-Data: 1 头,风险即刻落地:
- 后端收到该头后,是否对所有非幂等请求(登录、下单、改密等)直接返回
425 Too Early?不是 403,不是 500,必须是标准语义的 425 - 是否对允许的 GET 类 0-RTT 请求做了时间窗限制(如请求距 PSK 签发 ≤ 24 小时)或 nonce 校验?纯靠 TLS 层标识不安全
- 涉及用户状态变更的操作,是否叠加了业务层幂等键(如订单号去重)、token 有效性二次校验?不能仅依赖 TLS 层“已加密”就放行











