常见的web攻击方式有:SQL注入、XSS(跨站脚本攻击)、CSRF(跨站请求伪造)、DDOS(分布式拒绝服务)
攻击者向服务器提交恶意的SQL执行代码,程序在接受到代码后作为执行语句的一部分执行,改变了执行语句原有的逻辑,最终实现了攻击的意图。
例如输入: or 1=1
登陆时查询用户由:select * from users where username=’’ or 1=1
变成
select * from users
应对策略:
1)使用preparestatement预编译SQL语句,使用占位符来代替传入参数,这样语句结构不会发生变化
2)存储过程。存储过程(Stored Procedure)是一组完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过调用存储过程并给定参数(如果该存储过程带有参数)就可以执行它,也可以避免SQL注入攻击
3)前端预处理语句,过滤非法字符比如;#等
XSS是向网页中注入恶意脚本在用户浏览网页时在用户浏览器中执行恶意脚本的攻击方式。
XSS有两种:
1.反射型攻击(诱使用户点击一个嵌入恶意脚本的链接以达到攻击的目标,目前有很多攻击者利用论坛、微博发布含有恶意脚本的URL就属于这种方式)
2.持久型攻击(将恶意脚本提交到被攻击网站的数据库中,用户浏览网页时,恶意脚本从数据库中被加载到页面执行,QQ邮箱的早期版本就曾经被利用作为持久型跨站脚本攻击的平台)。
应对策略:
1.过滤特殊字符:对用户输入的内容进行特殊字符过滤。
2.请求头指定类型 Content-Type: text/javascript
攻击截取请求,盗取他人的身份(session、cookie)等,伪装该用户身份,执行相应的接口。
应对策略:
防范CSRF的主要手段是识别请求者的身份,主要有以下几种方式:
1.在表单中添加令牌(token)
2.验证码;
3.检查请求头中的Referer(前面提到防图片盗链接也是用的这种方式)。
令牌和验证都具有一次消费性的特征,因此在原理上一致的,但是验证码是一种糟糕的用户体验,不是必要的情况下不要轻易使用验证码,目前很多网站的做法是如果在短时间内多次提交一个表单未获得成功后才要求提供验证码,这样会获得较好的用户体验。
分布式拒绝服务攻击(Distributed Denial of Service),简单说就是发送大量请求是使服务器瘫痪。
DDoS攻击可以针对网络通讯协议的各层,手段大致有:TCP类的SYN Flood、ACK Flood,UDP类的Fraggle、Trinoo,DNS Query Flood,ICMP Flood,Slowloris类等等。一般会根据攻击目标的情况,针对性的把技术手法混合,以达到最低的成本最难防御的目的,并且可以进行合理的节奏控制,以及隐藏保护攻击资源。
应对策略:
阿里巴巴的安全团队在实战中发现,DDoS 防御产品的核心是检测技术和清洗技术。检测技术就是检测网站是否正在遭受 DDoS 攻击,而清洗技术就是清洗掉异常流量。而检测技术的核心在于对业务深刻的理解,才能快速精确判断出是否真的发生了 DDoS 攻击。清洗技术对检测来讲,不同的业务场景下要求的粒度不一样。
常用的预防策略:
1.增加图形验证
2.单IP请求次数限制
3.限制号码发送
这里,增加图形验证,对于在移动端的用户体验比较差,故我使用了后面两种方式:单IP请求次数限制、
限制号码发送。
我的实现思路:接口内对发送的号码进行限制,一天限制3次发送。接口拦截,对调用IP进行统计,多少时间内限制调用次数。
如果还有其他更好的思路,可以留言沟通。
接口限制发送次数主要部分代码:
long nTime = new Date().getTime();
boolean isUpdate = false;
//查询该号码是否已调用过,调用过,校验其上次发送时间以及调用次数
if (redisService.hasKey(phone+"_code")){
isUpdate = true;
long time = NumberUtils.toLong(redisService.hGet(phone+"_code","time").toString());
if ((nTime- time)<(1000*60)){
return ResultMessage.getResultMessage(ResponseStatus.RESPONSE_FAIL.getCode(),"请过一分钟再次发送");
}
long i = NumberUtils.toLong(redisService.hGet(phone+"_code","count").toString());
if (i>=3){
return ResultMessage.getResultMessage(ResponseStatus.RESPONSE_FAIL.getCode(),"该号码当天获取验证码次数已满3次");
}
}
//推送验证码
boolean bool = MessageUtils.sendPhoneMessage(phone, msgCode);
if (bool) {
userSms.setStatus(StatusContant.MsgStatusConstant.sendSuccess);
userSmsService.save(userSms);
//更新推送次数以及最新推送时间
if (isUpdate){
redisService.hIncr(phone+"_code","count",1);
redisService.hSet(phone+"_code","time",nTime);
} else { //添加记录
long remain = DateUtils.getRemainingSeconds();
Map map = new HashMap<>();
map.put("count",1);
map.put("time",nTime);
if (remain>0) {
redisService.hSet(phone + "_code", map,remain);
}
}
return ResultMessage.getResultMessage(200,"验证码发送成功");
}
接口拦截:
public class SendMsgIpInterceptor implements HandlerInterceptor {
private static String text = "{\"msgtype\": \"text\",\"text\": {\"content\": \"ip:[%s] 频繁调用短信发送接口 \"}}";
@Autowired
private RedisService redisService;
@Value("${web.hook}")
private String webHook;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = HttpContext.getReadIp(request);
if (StringUtils.isEmpty(ip)){
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
PrintWriter writer = response.getWriter();
writer.write(new ObjectMapper().writeValueAsString(new ResultMessage<>(400, "请求源IP不明")));
writer.close();
return false;
}
if (redisService.hasKey(ip+"_invoke")){
long count = NumberUtils.toLong(redisService.get(ip+"_invoke"));
if (count>10){
if (!redisService.hasKey(ip+"_invoke_day")){
long remain = DateUtils.getRemainingSeconds();
redisService.setEx(ip+"_invoke_day", String.valueOf(1),remain);
} else {
redisService.incr(ip+"_invoke_day");
//该IP频繁请求,可以通知系统管理员,直接在系统上拦截该IP
String str = String.format(text,ip);
HttpClientUtils.doPost(webHook, JSON.parseObject(str));
}
response.setContentType("application/json; charse`t=utf-8");
PrintWriter writer = response.getWriter();
writer.write(new ObjectMapper().writeValueAsString(new ResultMessage<>(400, "请勿频繁调用")));
writer.close();
return false;
} else {
redisService.incr(ip+"_invoke");
return true;
}
} else {
//设置值为10分钟
redisService.setEx(ip+"_invoke",String.valueOf(1),600);
}
return true;
}
}
注册接口拦截:
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Autowired
private SendMsgIpInterceptor sendMsgIpInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration addInterceptor = registry.addInterceptor(sendMsgIpInterceptor);
addInterceptor.addPathPatterns("/public/sendMobileMsg");
}
}
另外,项目购买的阿里消息服务,也提供了关于IP、号码限制的功能,可以在其控制台配置,这样可以更好的保护短信服务被恶意攻击。
参考文献:
https://www.cnblogs.com/sweetheartly/articles/9439789.html
https://www.cnblogs.com/wzj4858/p/8259944.html
https://www.cnblogs.com/morethink/p/8734103.html
https://blog.csdn.net/weixin_41910244/article/details/80544102
https://blog.csdn.net/qq_35387940/article/details/84391784
https://blog.csdn.net/LikeBoke/article/details/85328421