用 curl 把抓到的充值请求原样连发三次,三次都返回 200,账户余额加了三倍,服务端流水里三条记录除了入库时间几乎一模一样。碰到这种情况,我一般先确认改动有没有真的生效。

原因不复杂,接口只验了签名,签名是对请求内容做的哈希,内容不变签名就一直有效,既没有一次性随机数也没有时间窗,任何人拿到报文都能重复使用。

「防重放设计」这类改动最怕改一半,回滚脚本先准备好,再动生产。改完顺手把文档补一句,省得下次自己重新推一遍。

redis-cli SET nonce:8f3a 1 NX EX 300
# 返回 OK 才继续,返回 nil 说明这个请求已经来过

监控和日志的时间要对上,时区不一致会多绕很大一圈。临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。

具体做法是,请求里加上 nonce 和时间戳,超出时间窗五分钟就直接拒绝;把 nonce 写进 Redis 里,用 setnx 加过期时间做一次性判断,重复的直接拒;签名覆盖 nonce 和时间戳,防止这两个值中途被替换掉。

最后用这个标准收尾,同一个请求包第二次发送返回 409,首次请求仍然能正常完成并记账。

作者 9823189