防重放攻击实战
重放攻击指攻击者截获一次合法请求后,原样再次发送以冒充合法调用。A7验证的握手模型内置了两道防线,客户端也可自行加固。
一、服务端已有的防线
1. 时间戳窗口(±300 秒)
每次 Init 与 Handle 都携带 TimeStamp(Unix 秒级)。服务端校验 abs(服务器时间 - TimeStamp) <= 300,超出直接返回 1014 时间戳已过期。这保证了一份截获的请求最多在 5 分钟内可被重放,过期自动失效。
- 防御效果:超出 5 分钟的重放请求 100% 被拒。
- 前提:客户端服务器时间不能偏差过大。若用户机器时间不准,建议客户端先校时。
2. 签名与密文绑定
Sign = MD5(ApiPassword + TimeStamp + Data),Data 是 RC4 密文。攻击者即便截获请求,没有 ApiPassword(只在 Init 阶段用 AppKey 换一次,且 AppKey 不进业务流量)就无法重新计算签名,因此无法篡改或重新生成请求。
为什么 AppKey 不出现在业务流量里
业务请求通过 X-Api-Password 头或 Data 密文传递,全程不含 AppKey。即使业务流量被抓包,攻击者也拿不到永久凭据,只能复用同一份 5 分钟内的密文。
二、客户端加固建议
一次性随机数(nonce)
当前后端未强制校验 nonce。若你的业务对重放极其敏感,可在明文参数中附加一个客户端生成的随机值(如 &Nonce=<uuid>),并在本地维护「已用 nonce 集合」,对重复 nonce 直接丢弃。
import uuid
resp = client.call("SingleLogin", {
"Card": card, "Mac": mac, "Nonce": uuid.uuid4().hex
})后端不校验 nonce
加入 nonce 仅能防止客户端自身重复发送;后端不会拒绝重复 nonce。真正的服务端重放防护仍依赖时间戳窗口。不要在文档中声称后端做了 nonce 校验。
TLS 证书固定
对高安全场景,可在客户端做证书固定(Certificate Pinning),防止中间人劫持后改包。但这会提高维护成本(证书轮换时需更新固定值)。
不要把 ApiPassword 落盘
ApiPassword 每次启动重新 Init 获取即可。若写入文件,一旦文件泄露,攻击者可构造任意请求直到该 ApiPassword 被开发者重置。
三、常见误区
| 误区 | 事实 |
|---|---|
| 密文是静态的,可永久重放 | 错误。密文绑定 TimeStamp,超过 ±300s 被拒 |
| 抓包能拿到 AppKey | 错误。业务流量不含 AppKey |
| 后端校验了 nonce | 错误。当前后端未实现 nonce 校验 |