共计 1960 个字符,预计需要花费 5 分钟才能阅读完成。
从电商支付漏洞看 Token 重放危害
去年我们团队遇到一个典型案例:攻击者利用 Burp Suite 拦截用户支付请求,将包含身份令牌的请求重复发送 100 次,导致用户账户被扣款 50 万元。这种攻击之所以能成功,是因为:

- 支付接口仅验证 Token 有效性
- 没有请求新鲜度校验机制
- 服务端缺少请求唯一性标识
三大防御方案原理对比
1. 时间戳窗口验证
- 原理:请求携带当前时间戳,服务端校验时间差
- 优点:实现简单,无需存储
- 缺点:依赖时钟同步,无法防御窗口期内的重放
2. Nonce 缓存机制
- 原理:每次请求生成唯一随机数,服务端缓存校验
- 优点:完全防止重放
- 缺点:需要存储空间,集群环境需共享缓存
3. 签名验证机制
- 原理:用密钥对请求要素生成签名
- 优点:防篡改 + 防重放
- 缺点:计算开销较大
Spring Boot 防御实现
请求签名生成
/**
* 生成 HMAC-SHA256 签名
* @param secret 密钥
* @param params 包含 timestamp+nonce 的请求参数
*/
public String generateSignature(String secret, Map<String,String> params) {StringJoiner sj = new StringJoiner("&");
params.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(e -> sj.add(e.getKey()+"="+e.getValue()));
Mac sha256 = Mac.getInstance("HmacSHA256");
sha256.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
return Hex.encodeHexString(sha256.doFinal(sj.toString().getBytes()));
}
Nonce 校验器
@Repository
public class NonceValidator {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 校验 Nonce 唯一性
* @return true 表示首次使用
*/
public boolean checkNonce(String nonce, long expireSeconds) {
Boolean result = redisTemplate
.opsForValue()
.setIfAbsent("nonce:"+nonce, "1", expireSeconds, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
}
时间漂移补偿
// 在拦截器中处理时间差
long clientTime = Long.parseLong(request.getHeader("X-Timestamp"));
long serverTime = System.currentTimeMillis() / 1000;
// 允许±3 分钟时间差
if (Math.abs(serverTime - clientTime) > 180) {throw new InvalidTimestampException();
}
性能优化实践
Redis 集群配置建议
- 采用 CRC16 分片模式部署 6 节点
- 设置 maxmemory 8GB + LRU 淘汰策略
- 测试数据:单个 Nonce 校验平均耗时 1.2ms
签名计算性能
| 并发数 | 平均耗时 (ms) | CPU 使用率 |
|---|---|---|
| 100 | 15 | 22% |
| 1000 | 28 | 67% |
| 5000 | 112 | 98% |
安全增强方案
密钥轮换策略
- 准备两套密钥 (active/standby)
- 请求头携带密钥版本号
- 每月 1 号凌晨自动切换
防时序攻击比较
// 使用 MessageDigest.isEqual 替代 equals
public static boolean safeEquals(String a, String b) {
return MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8),
b.getBytes(StandardCharsets.UTF_8)
);
}
实战演练指南
Burp 重放攻击实验
- 启动未防护的 DEMO 服务
- 用 Burp 拦截含 Token 的 POST 请求
- 右键选择 ”Send to Repeater”
- 多次重放观察响应结果
Wireshark 验证防护
- 配置 SSL 解密
- 过滤目标 IP 和端口
- 对比防护前后请求特征:
- 未防护:相同 Raw Data 反复出现
- 已防护:每次 Nonce 和签名不同
总结建议
实际部署时建议组合使用三种方案:用时间戳做初步过滤,Nonce 保证绝对唯一,签名防止参数篡改。我们在金融级应用中采用该方案后,成功拦截了所有重放攻击尝试,系统额外开销控制在 8% 以内。
正文完
发表至: 网络安全
近一天内
