一、重放攻击概述
1.1 什么是重放攻击
重放攻击(Replay Attack)是网络安全中一种常见的攻击手段,攻击者通过截获并重新发送合法用户的请求数据包来欺骗服务器。与需要破解加密信息的攻击方式不同,重放攻击只需原样重复有效请求即可实施攻击。
1.2 重放攻击的危害场景
-
金融交易:重复执行转账操作
-
电商系统:恶意重复提交订单
-
身份认证:截获登录凭证后重复使用
-
API调用:重复获取敏感数据或执行操作
1.3 重放攻击的特点
-
不需要解密:直接使用截获的原始数据
-
难以察觉:请求本身是合法的
-
危害严重:可造成资金损失、数据泄露等后果
二、Spring Boot 防御方案详解
2.1 时间戳+签名方案
实现原理
通过为每个请求添加时间戳和数字签名,确保请求的时效性和完整性。
核心代码实现
public class TimestampSignatureFilter extends OncePerRequestFilter {
private static final long DEFAULT_EXPIRE_TIME = 300000; // 5分钟
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 验证时间戳
String timestamp = request.getHeader("X-TIMESTAMP");
if (isTimestampInvalid(timestamp)) {
sendError(response, "Invalid timestamp");
return;
}
// 验证签名
if (!validateSignature(request)) {
sendError(response, "Invalid signature");
return;
}
chain.doFilter(request, response);
}
private boolean isTimestampInvalid(String timestamp) {
try {
long requestTime = Long.parseLong(timestamp);
return Math.abs(System.currentTimeMillis() - requestTime) > DEFAULT_EXPIRE_TIME;
} catch (NumberFormatException e) {
return true;
}
}
private boolean validateSignature(HttpServletRequest request) {
// 实现签名验证逻辑
return true;
}
}
方案优势
-
实现简单
-
有效防止过时请求被重放
-
保证请求完整性
适用场景
-
RESTful API接口
-
需要保证时效性的操作
2.2 Nonce(一次性令牌)方案
实现原理
为每个请求生成唯一标识,服务器验证该标识是否已被使用过。
核心组件
@Service
public class NonceService {
private final Cache<String, Boolean> nonceCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(100000)
.build();
public boolean isNonceValid(String nonce) {
if (nonceCache.getIfPresent(nonce) != null) {
return false;
}
nonceCache.put(nonce, true);
return true;
}
}
方案优势
-
完全防止请求被重复使用
-
实现相对简单
-
可结合其他安全措施
适用场景
-
支付系统
-
关键业务操作
-
高安全性要求的接口
2.3 序列号方案
实现原理
为客户端分配递增序列号,服务器验证序列号的连续性。
实现示例
public class SequenceValidator {
private final ConcurrentMap<String, AtomicLong> clientSequences = new ConcurrentHashMap<>();
public boolean validate(String clientId, long currentSequence) {
AtomicLong lastSequence = clientSequences.computeIfAbsent(
clientId, k -> new AtomicLong(0));
long expected = lastSequence.get() + 1;
if (currentSequence >= expected) {
lastSequence.set(currentSequence);
return true;
}
return false;
}
}
方案优势
-
防止请求乱序
-
可检测中间人攻击
-
适合有状态通信
适用场景
-
物联网设备通信
-
客户端-服务器长连接
-
需要保证顺序的业务流程
三、综合防御策略
3.1 基础安全配置
HTTPS强制启用
# application.properties
server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=changeit
请求头安全配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
}
3.2 高级防御措施
请求限流保护
@Configuration
public class RateLimitConfig {
@Bean
public FilterRegistrationBean<RateLimitFilter> rateLimitFilter() {
FilterRegistrationBean<RateLimitFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new RateLimitFilter());
registration.addUrlPatterns("/api/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
public class RateLimitFilter implements Filter {
private final RateLimiter limiter = RateLimiter.create(100); // 100请求/秒
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (limiter.tryAcquire()) {
chain.doFilter(request, response);
} else {
((HttpServletResponse)response).sendError(429, "Too many requests");
}
}
}
请求体完整性校验
@RestControllerAdvice
public class ValidationHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
fieldError -> fieldError.getDefaultMessage() != null
? fieldError.getDefaultMessage()
: "Invalid value"));
return ResponseEntity.badRequest().body(errors);
}
}
四、最佳实践与建议
4.1 方案选择指南
| 方案类型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 时间戳+签名 | 普通API接口 | 实现简单,时效性好 | 依赖时间同步 |
| Nonce | 支付等高安全场景 | 绝对防重放 | 需要存储状态 |
| 序列号 | 设备通信 | 防止乱序 | 需要客户端配合 |
4.2 实施建议
-
组合防御:推荐时间戳+Nonce+签名的组合方案
-
密钥管理:
-
使用Spring Cloud Config集中管理
-
定期轮换密钥
-
不同服务使用不同密钥
-
-
监控体系:
@Aspect
@Component
public class SecurityMonitor {
@AfterThrowing(pointcut="execution(* com.example..*.*(..))",
throwing="ex")
public void logSecurityException(SecurityException ex) {
// 发送告警通知
}
}
4.性能优化:
-
使用Caffeine等高性能缓存
-
非关键接口可放宽检查
-
考虑分布式场景下的同步问题
4.3 常见问题解决方案
问题1:时间不同步导致验证失败
-
方案:部署NTP时间同步服务
-
容错:允许小范围时间偏差
问题2:Nonce存储占用过大
-
方案:使用BloomFilter等概率数据结构
-
优化:设置合理的过期时间
问题3:移动端网络延迟
-
方案:适当延长时间窗口
-
补偿:客户端重试机制
五、总结
Spring Boot应用中防御重放攻击需要建立多层次的安全防护体系。本文介绍的三种核心方案各有特点,实际应用中应根据业务场景灵活选择和组合。同时,配合HTTPS、请求校验、限流等基础安全措施,可以构建更加完善的防御系统。
关键要点总结:
-
理解重放攻击的原理和危害是防御的基础
-
时间戳方案适合大多数API接口
-
Nonce方案提供最高级别的防护
-
序列号方案适合有状态通信场景
-
组合使用多种方案效果最佳
-
完善的监控体系是长期安全的保障
随着攻击手段的不断演进,安全防护措施也需要持续更新。建议定期进行安全审计和渗透测试,确保防护措施始终有效。

340

被折叠的 条评论
为什么被折叠?



