后端面试:如何安全处理 HTTP 425 Too Early 与 0-RTT 重放?
题干与适用场景
支付 API 启用了 TLS 1.3 会话恢复。部分请求在连接尚未完成握手时到达,网关返回 425 Too Early。请说明哪些请求可以使用早期数据、如何避免重放副作用、代理怎样传递信号,以及客户端何时可以重试。
面试官考察点
- 是否理解 0-RTT 追求延迟,却不能提供普通握手同等的重放保护。
- 能否区分 425、网络超时和业务拒绝,并正确使用
Early-Data: 1。 - 能否把幂等键、去重窗口、网关策略和服务端状态机连成闭环。
- 能否用指标与演练证明重试没有重复扣款。
回答前需要澄清的问题
- 客户端、CDN、网关和源站谁终止 TLS,谁能判断请求是否处于早期数据?
- 请求是 GET、查询还是会扣款、发货、写消息的副作用操作?
- 是否有全局唯一幂等键,去重记录保存多久,跨区域是否共享?
- 425 是否由网关统一返回,客户端 SDK 是否理解它并能重建请求体?
- 重试截止时间、支付授权有效期和用户可见状态如何对齐?
30 秒回答框架
0-RTT 让客户端在 TLS 1.3 恢复会话时提前发送应用数据,但该数据可能被攻击者重放。服务端对有副作用的请求默认拒绝早期数据,代理可用 Early-Data: 1 表示请求经过早期数据,并返回 425。客户端完成普通握手后只重试可安全重放的请求;支付类请求必须先用幂等键查询结果或等待业务确认,不能把 425 当作扣款失败。
分步骤深入解答
第一步:定义 0-RTT 边界
TLS 1.3 会话恢复允许客户端发送 early data,以减少一次往返延迟。服务器不能把它当作新鲜且不可重放的证明;攻击者可能复制同一段数据,在允许的窗口内再次发送。
第二步:按副作用分类
公开 GET、只读查询或具备严格幂等语义的请求可以在满足协议和业务条件时考虑 early data。扣款、创建订单、发放权益、发送消息等操作默认禁用,除非服务端有可靠的幂等键与原子去重。
第三步:理解 Early-Data 与 425
支持早期数据的中间层可在转发请求时加入 Early-Data: 1。源站不愿承担重放风险时返回 425,表示请求过早,客户端应在握手完成后重新发送。没有该头不代表请求绝对没有重放风险,端到端部署仍需确认各跳行为。
第四步:设计网关策略
网关按方法、路径和认证类型建立白名单。对支付、库存和权限变更路径直接禁止 early data;对只读路径可放行并记录连接状态。网关不能把 425 改成普通 500,否则 SDK 无法采取正确动作。
第五步:设计幂等与去重
客户端为每次业务意图生成不可预测的幂等键,服务端在提交副作用和写入去重记录时使用同一事务或等价原子机制。重复键返回第一次结果,不能重新执行扣款。去重 TTL 覆盖最大网络重试、队列延迟和对账窗口。
第六步:规定安全重试
收到 425 后先建立完整连接,再重放仍在截止时间内且请求体可重建的请求。GET 可自动重试;带幂等键的支付请求先查询幂等状态再决定重试;没有幂等保障的写请求交给业务确认。对同一意图设置一次重试上限和退避。
第七步:验证与观测
记录 early-data 请求数、425 率、按路径的重试率、重复幂等键、扣款成功与退款对账差异。用代理链和故障注入测试:早期数据被拒绝、普通握手重试、客户端超时、重复请求以及跨区域去重。验收必须证明每个业务意图最多产生一次副作用。
高质量示范回答
我先把支付写路径加入 early-data 禁止名单。TLS 终止层若收到早期数据,向源站传 Early-Data: 1;源站对这类写请求返回 425,并保留可诊断的请求 ID。客户端完成普通握手后不会盲目重放:它用幂等键查询订单或扣款状态,若状态未知才在截止时间内重试一次。服务端把幂等键、业务结果和去重记录原子落库,跨区域共享去重边界。只读 GET 可以自动重试,所有路径都监控 425、重复键和支付对账差异,以演练证明没有重复扣款。
常见错误
- 认为 TLS 1.3 的 0-RTT 天然防重放。
- 收到 425 就无限重试,或把它改写成 500。
- 只按 HTTP 方法判断安全,忽略某些 GET 也可能触发副作用。
- 幂等键只存在客户端,服务端没有原子去重和结果保存。
- 用“请求未到达”假设处理超时,未先查询业务状态。
追问及应对
追问一:425 与 503 有什么区别?
425 针对早期数据的重放风险,暗示完成握手后可以重新评估;503 表示服务暂时不可用,重试依据是服务能力和 Retry-After,两者不能互换。
追问二:Early-Data 头由谁添加?
理解该机制的中间层在把早期数据转发给源站时添加 Early-Data: 1。部署必须核实 TLS 终止点和代理是否保留或伪造该信号。
追问三:支付请求有幂等键就能放心用 0-RTT 吗?
不能直接放心。还要保证键不可预测、去重记录原子持久化、跨区域一致性和 TTL 足够长,并验证重复键返回同一结果。
追问四:客户端超时后为什么先查询?
超时只说明客户端没有收到结果,不能证明服务端没有提交。查询幂等状态可以区分已成功、处理中和未执行,避免重复扣款。
追问五:如何做灰度?
先在只读路径和小流量客户端启用,按路径记录 425 与重试成功率;任何重复副作用、异常对账或跨代理信号缺失都应自动关闭 early data。