概念
重放攻击(英语:replay attack,或称为回放攻击)是一种恶意或欺诈的重复或延迟有效数据的网络攻击形式。 这可以由发起者或由拦截数据并重新传输数据的对手来执行,这可能是通过IP数据包替换进行的欺骗攻击的一部分。 这是“中间人攻击”的一个较低级别版本。
这种攻击的另一种描述是: “从不同上下文将消息重播到安全协议的预期(或原始和预期)上下文,从而欺骗其他参与者,致使他们误以为已经成功完成了协议运行。”
说白了,中间人拿着你的请求数据多次请求,多次请求中数据都是一样的
解决方案
加时间戳,在请求头或者是请求体中加。然后在全局统一的过滤器或者是拦截器对请求地址,参数,时间戳进行拼接,拼接之后使用MD5哈希过后以此作为Key判断Redis中是否存在相同的Key,如果存在则认定为是重放攻击,拦截不让放行,否则的话就不是,此时需要存入Redis中,并且设置过期时间。过期时间不宜过长,具体失效时间看个人
代码实现
/**
* @Author: 资深高级Java开发工程师
* @CreateDate: 2023/8/6
* @Description: 请求重放过滤器
*/
@Order(1)
@WebFilter(urlPatterns = "/*", filterName = "PlaybackFilter", asyncSupported = true)
@Component
public class PlaybackFilter implements Filter {
private static final String TIME_STAMP = "timeStamp";
private static final int DEFAULT_EXPIRE_TIME = 30;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private RedisPrefix redisPrefix;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest r = (HttpServletRequest) request;
String header = r.getHeader(TIME_STAMP);
String requestUrl = r.getRequestURI();
String requestBody = IOUtils.toString(request.getReader());
MD5 md5 = MD5.create();
String summary = requestUrl + requestBody + header;
String key = md5.digestHex(summary);
String redisKey = redisPrefix.getPlaybackKey() + key;
Boolean hasKey = redisTemplate.hasKey(redisKey);
//如果存在Key则不放行,return
if (Boolean.TRUE.equals(hasKey)) {
return;
}
execLua(redisKey);
chain.doFilter(request, response);
}
/**
* 使用Lua脚本执行原子性的Redis操作,
* 如果key不存在则设置value为1并且设置过期时间为30秒,
*/
private void execLua(String key) {
RedisScript<Long> script = new DefaultRedisScript<>("if redis.call('exists', KEYS[1]) == 0 then\n" +
" redis.call('set', KEYS[1], 1, 'ex', " + DEFAULT_EXPIRE_TIME + ")\n" +
" return 1\n", Long.class);
redisTemplate.execute(script, Collections.singletonList(key));
}
}