欢迎各位 前(提)来(出)讨(意)论(见)。
1、重放攻击简介
重放攻击(Replay Attacks)又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。
- 举个简单的小例子。
我们往系统里面插入一条数据,如果此次操作的数据包被攻击者获取,又恰巧后台没有对重复内容进行验证,那么攻击者就可以利用该数据包重复的发起请求,无限制的往数据库插入数据,即使请求包是加密的也不影响,从而造成资源浪费。
再可能用户支取了一笔存款,攻击者完全可以多次发送这条消息从而偷窃存款。
2、重放攻击演示
- 使用工具:Fiddler:一款用于抓包的工具
- 重放演示
(1)首先在系统添加一条记录
(2)使用抓包工具检测到该记录,获取到数据包
(3)抓包工具上选中该记录,点击“Replay”,也就是进行数据重放。处理完成后会再次生成一个数据包,这就是模拟了在系统中添加数据进行了一次重放操作。
选中该记录,点击“Replay”,进行数据重放
(4)返回系统进行查询,发现多了一条重放的记录
3、解决方案
百度搜索了一下,可以通过加随机数、时间戳或者是加流水号的方式进行处理(三种方式可以单独使用也可以组合使用)。
此次记录在项目中使用加时间戳的方式进行处理。
-
处理逻辑
(1)前台在发送HTTP请求时在Header中添加时间戳认证;
(2)后台在处理请求时获取该认证的时间戳,判断该请求是否在短时间内进行了重复请求。
-
前台改动
找到处理http请求的地方,在请求头中Header中添加时间戳。
config.headers['_time'] = new Date().getTime()
- 后台改动
// 重放攻击漏洞 解决
// 获取http请求类型
String type = ((HttpServletRequest) servletRequest).getMethod().toLowerCase();
// 获取添加的时间戳
String timeForHeader = ((HttpServletRequest) servletRequest).getHeader("_time");
if (!"get".equals(type) && !StringUtils.isEmpty(timeForHeader)) {
String requestUri = httpServletRequest.getRequestURI();
// 将uri、时间戳、token值作为单用户的请求识别,作为key,进行验证
String replayKey = String.format("replay_%s_%s_%s", requestUri, timeForHeader, token);
if (redisUtils.hasKey(replayKey)) {
// 如果存在 表示重放攻击的
log.info("检测到请求可能是重放攻击,不进行处理");
return;
} else {
// 如果不存在,将时间戳值作为value放到redis中, 避免redis存储越来越多,可将该记录值设为临时的
redisUtils.set(replayKey, timeForHeader, 1, TimeUnit.MINUTES);
}
}
(1)在过滤器Filter中获取请求头中指定的认证“_time”的值,也就是前台赋值的时间戳;
(2)将uri、时间戳、token组成key,时间戳为value;
(3)验证该key在redis库中是否存在,如果存在,当成重放攻击的请求,不予处理。不存在则将该key与value存放到redis缓存中,该类型的记录设定1分钟的缓存时间,避免数据增多造成存储浪费。
(4)上面代码中的处理不够严谨,将基于请求的uri、时间戳和token值缓存到redis中,由于缓存时间设置了1分钟,1分钟后该记录在redis库中会被删除,那么在1分钟后再次进行重放攻击还是可以插入成功数据的。这里可以在检查完redis的缓存后,将时间戳的值转换为时间,再与当前时间进行比较,如果两个时间差大于1分钟,同样视为重放攻击,不予处理即可。
4、重放攻击验证
再次选中添加记录的数据包,点击“Replay”,进行重放试验,查询页面数据,依然是两条记录(其中一条是开始未改动前重放进去的记录)。