需求分析
最近很闲,就写个功能型小程序,用uniapp写的,没有使用云数据库,就自己买了个腾讯的云服务器,很便宜,在买个域名,十多天完成备案,把这些都准备好就可以开始写了,首先就想到了2个功能,短视频解析和运动步数修改,这些代码都是网上参考原理写出来的。因为我懒得弄那些加密什么的,本以为就自己用,没想到居然还有人使用xss注入攻击我网站,真的无语。后台使用的是jeesite社区版的,功能很强大,那些sql注入都被拦截下来了,我看了也就没理会,以为他会放弃的,可没想到攻击者一个接着一个来,都不消停了,看来是不做点什么是不行了,我服务器很小,经不起他这样攻击,关键还利用了log4j的漏洞来,幸好这个框架没有使用这个日志框架,不然还真会出问题。
被攻击的日志:
开始
我做了2个操作:接口用MD5加密验证,利用redis存储ip判断在规定时间内的请求次数。
首先md5加密是利用时间戳和关键字段加密,然后和密钥再一次加密,除非他拿到我小程序的源码不然是不可能破解的,不过他还是有可能拿到我的源码的
增加超过时间的判断,防止他拿着我密钥和时间戳来通过请求验证
public static boolean verify(HttpServletRequest request,String s){
try {
String sign = request.getParameter("sign");
String times = request.getParameter("time");
if (System.currentTimeMillis()-Long.parseLong(times)>20000){
System.out.println("验证超时!");
return false;
}
String md5 = SignUtils.getMd5(SignUtils.getMd5(s+ times) + 密钥);
return Objects.equals(md5, sign);
}catch (Exception e){
return false;
}
}
利用redis速度快的特点缓存ip来判断
代码来自
/**
* 自定义注解,用于拦截过于频繁的请求
*/
@Retention(RetentionPolicy.RUNTIME) //注解将被编译器记录在类文件中 在运行时保留VM,因此可以反读。
@Target(ElementType.METHOD) //指定在何处写入注解的合法位置
public @interface AccessLimit {
int seconds();
int maxCount();
}
/**
* 自定义拦截器
*/
@Component
public class AccessLimtInterceptor implements HandlerInterceptor {
//springboot自带的redis模板
@Autowired
private RedisTemplate redisTemplate;
//在业务处理器处理请求之前被调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断handler是否为HandlerMethod的实例
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (null == accessLimit) {
return true;
}
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
String ip=request.getRemoteAddr();
String key = request.getServletPath() + ":" + ip ;
Integer count = (Integer) redisTemplate.opsForValue().get(key);
if (null == count || -1 == count) {
redisTemplate.opsForValue().set(key, 1,seconds, TimeUnit.SECONDS);
return true;
}
//过期时间
Long expire = redisTemplate.opsForValue().getOperations().getExpire(key);
if (expire==-1){
redisTemplate.delete(key);
}
if (count < maxCount) {
count = count+1;
redisTemplate.opsForValue().set(key, count,0);
return true;
}
throw new ResultException(1438,"您的IP:"+ip+" 当前请求次数为:"+count+" 过期时间:"+expire+" 该次请求已经超过了规定时间范围内请求的最大次数","");
}
return true;
}
}
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Autowired
private AccessLimtInterceptor accessLimtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLimtInterceptor).addPathPatterns("/拦截路径")
.excludePathPatterns("/不被拦截路径 通常为登录注册或者首页");
super.addInterceptors(registry);
}
}
这样就可以在接口上使用注解了
|