SpringBoot + 防重放攻击 + 请求签名:API 接口防刷、防篡改、防重复提交

今天咱们聊聊一个在API安全中非常关键的话题:如何保护我们的接口不被恶意攻击。

API安全面临的威胁

在我们的日常开发工作中,经常会遇到这样的安全威胁:

  • 接口被刷:恶意用户通过脚本大量调用接口,消耗服务器资源
  • 数据被篡改:请求参数在传输过程中被中间人修改
  • 重复提交:同一笔交易被恶意重复提交,造成经济损失
  • 身份冒充:攻击者伪造合法用户身份进行操作

传统的认证方式往往只能解决身份验证问题,对于请求的完整性和防重放攻击无能为力。今天我们就来聊聊如何构建一套完整的API安全防护体系。

核心防护机制

1. 请求签名机制

请求签名就像是给每个请求贴上一个独一无二的"身份证",确保请求的完整性和真实性:

  • 客户端将请求参数按照约定规则拼接并加密生成签名
  • 服务端收到请求后,按照同样规则验证签名
  • 如果签名不匹配,说明请求被篡改,直接拒绝

2. 防重放攻击

防重放攻击的核心是确保每个请求只能被处理一次:

  • 为每个请求生成唯一的nonce(随机数)
  • 结合时间戳,确保请求在有效期内
  • 服务端记录已处理的请求,防止重复处理

3. 多层防护

我们构建的是一个多层次的安全防护体系:

  • 接入层:IP限流、请求频率控制
  • 认证层:身份验证、权限校验
  • 应用层:参数校验、业务逻辑验证

实现方案详解

1. 签名算法设计

签名算法是整个安全体系的核心,我们采用HMAC-SHA256算法:

public class SignatureUtil {
    
    public static String generateSignature(Map<String, String> params, String secretKey) {
        // 参数排序
        String sortedParams = sortParameters(params);
        
        // 拼接待签名字符串
        String signStr = sortedParams + "&secret=" + secretKey;
        
        // 生成签名
        return hmacSHA256(signStr, secretKey);
    }
    
    private static String sortParameters(Map<String, String> params) {
        // 按照参数名排序并拼接
        return params.entrySet().stream()
                .filter(entry -> !"sign".equals(entry.getKey())) // 排除签名字段
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
    }
}

2. 防重放机制实现

为了防止请求被重复使用,我们引入时间窗口和唯一标识:

@Component
public class ReplayAttackPrevention {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public boolean validateNonce(String nonce, long timestamp) {
        // 检查时间戳是否在有效期内(如5分钟内)
        long currentTime = System.currentTimeMillis();
        if (currentTime - timestamp > 5 * 60 * 1000) {
            return false; // 超过有效期
        }
        
        // 检查nonce是否已使用过
        String key = "nonce:" + nonce;
        Boolean exists = redisTemplate.hasKey(key);
        if (Boolean.TRUE.equals(exists)) {
            return false; // nonce已存在,防止重放
        }
        
        // 设置nonce有效期,防止内存占用过大
        redisTemplate.opsForValue().set(key, "1", Duration.ofMinutes(10));
        return true;
    }
}

3. 拦截器集成

将安全验证逻辑集成到Spring的拦截器中:

@Component
public class ApiSecurityInterceptor implements HandlerInterceptor {
    
    @Autowired
    private SignatureValidator signatureValidator;
    
    @Autowired
    private ReplayAttackPrevention replayProtection;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 1. 验证签名
        if (!signatureValidator.validate(request)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.getWriter().write("{\"error\":\"Invalid signature\"}");
            return false;
        }
        
        // 2. 防重放验证
        String nonce = request.getHeader("X-Nonce");
        String timestamp = request.getHeader("X-Timestamp");
        
        if (!replayProtection.validateNonce(nonce, Long.parseLong(timestamp))) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            response.getWriter().write("{\"error\":\"Replay attack detected\"}");
            return false;
        }
        
        return true;
    }
}

4. 客户端实现

客户端需要按照约定生成签名和必要参数:

public class ApiClient {
    
    public ApiResponse callApi(ApiRequest request, String appKey, String secretKey) {
        // 添加必要参数
        request.addParam("app_key", appKey);
        request.addParam("timestamp", String.valueOf(System.currentTimeMillis()));
        request.addParam("nonce", UUID.randomUUID().toString());
        
        // 生成签名
        String signature = SignatureUtil.generateSignature(request.getParams(), secretKey);
        request.addParam("sign", signature);
        
        // 发送请求
        return httpClient.post(request);
    }
}

高级防护策略

1. IP限流

结合令牌桶算法对IP进行限流:

@Component
public class IpRateLimiter {
    
    public boolean tryAcquire(String ip, int permits) {
        String key = "rate_limit:" + ip;
        // 使用Redis的Lua脚本实现原子操作
        return executeRateLimitScript(key, permits);
    }
}

2. 用户行为分析

通过分析用户的历史行为,识别异常请求模式:

  • 频率异常:单位时间内请求次数过多
  • 时间异常:在非正常时间段大量请求
  • 参数异常:相同参数重复提交

3. 动态密钥管理

实现密钥的动态更新,提高安全性:

  • 定期更换密钥
  • 支持多版本密钥共存
  • 密钥失效后的优雅降级

最佳实践建议

  1. 密钥安全管理:密钥不要硬编码在代码中,使用配置中心或密钥管理服务
  2. 性能考虑:安全验证逻辑尽量高效,避免影响正常业务性能
  3. 监控告警:对异常请求进行监控和告警
  4. 日志记录:详细记录安全相关的操作日志

通过这套完整的安全防护体系,我们可以有效抵御各种API攻击,保护我们的系统免受恶意侵害。


以上就是本期分享的内容,希望对你有所帮助。更多技术干货,请关注服务端技术精选,我们下期再见!


标题:SpringBoot + 防重放攻击 + 请求签名:API 接口防刷、防篡改、防重复提交
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/01/29/1769577061239.html

    0 评论
avatar