【Web安全】Session防爆破的实现

前置内容:什么是Session爆破 (请参考 http://www.cnblogs.com/phpstudy2015-6/p/6776919.html

session爆破的基本流程如下图所示:
在这里插入图片描述
本文将解决攻击者在第3步中使用暴力破解的方式获取合法的session:

实现思路:
攻击者通过携带不同的session向服务端发送请求以达到获取合法回话。因此可以为服务器端增加一个过滤器来判断某一IP是否存在潜在的攻击行为。

  1. SessionBlastManager类:此类的作用是封装所需的功能性操作
public class SessionBlastManager{
	private static final Logger logger = LoggerFactory.getLogger(SessionBlastManager.class);
	private static Map<String,SessionBlastRequest> session_protect_map = new ConcurrentHashMap<>();
	private static Map<String,Long> limited_ip_map = new ConcurrentHashMap<>();
	//判断IP是否被限制访问
	private static boolean hasLimitIp(String remoteAddress){
		Long time = limited_ip_map.get(remoteAddress);
		if(time==null){
			return false;
		}
		return true;
	}

//检查Session爆破,返回是否需要限制IP地址访问
	public static boolean checkSessionBlastANdLimitIp(String remoteAddress,String sessionId){
//判断当前IP是否被限制请求
	if(hasLimitIp(remoteAddress)){
		logger.info(remoteAddress + "has be limited");
		return true;
	}
	//判断当前请求的session是否有效
	LoginInfo loginInfo = Constants.sessionId_loginInfo_map.get(sessionId);
	//判断是否存在登陆信息
	if(loginInfo != null){
		return false;
	}
	long second = System.currentTimeMillis()/1000;
	if(session_protect_map.get(remoteAddress)== null){
		session_protect_map.put(remoteAddress,new SessionBlastRequest());
	}
	SessionBlastRequest sessionBlastRequest = session_protect_map.get(remoteAddress);
	int count = 0 ;
	if(sessionBlastRequest.getWorryRequestNum()!=null){
		Collection<Integer> values = sessionBlastRequest.getWorryRequestNum().values();
		if(values !=null){
			Iterator<Integer> integerIterator = values.itertor();
			while(integerIterator.hasNext()){
				count += integerIterator.next();
			}
		}
	}else{
		sessionBlastRequest.setWorryRequestNum(new ConcurrentHashMap<Long,Integer>());
	}
	ConcurrentHashMap worryMessage = sessionBlastRequest.getWorryRequestNum();
	if(count < 60 ){
		if(worryMessage.get(second) == null){
			worryMessage.put(second,1);
		}else{
			int num = (int)worryMessage.get(second);
			worryMessage.put(second,num+1);
		}
	}else{
		limited_ip_map.put(remoteAddress,System.currentTimeMillis());
		logger.info("this Ip may have an attack intent and will be prohibited form accessing the server");
		return true;
	}
	return false;
	}

	//当请求超过2分钟后删除此请求信息
	public static void deleteTimeoutErrorRequest(){
		long currentTimeMillis = System.currentTimeMillis();
		if(session_protect_map.size() > 0){
			Iterator<Map.Entry<String,SessionBlastRequest>> iterator = session_protect_map.entrySet().iterator();
			while(iterator.hasNext()){
				Map.Entry<String,SessionBlastRequest> user = iterator.next();
				if(user != null && user.getValue().getWorryRequestNum()!= null){
					Iterator<Map.Entry<Long,Integer>> iterator1 =user.getValue().getWorryRequestNum().entrySet().iterator();
					if(iterator1 != null){
						while(iterator1 != null){
							Map.Entry<Long,Integer> next1 = iterator1.next();
							if(next1 != null && currentTimeMills/1000 - next1.getKey() >= 120){
								logger.info("clean the invalid message:"+next1.getKey());
								iterator1.remove();
							}
						}
					}
				} 
			}
		}
		return;
	}

 //IP被限制5分钟后解除对此IP的限制
	public static void unlimitIp(){
		if(limited_ip_map.size() > 0){
			long currentTimeMillis = System.currentTimeMillis();
			Iterator<Map.Entry<String,Long>> iterator = limited_ip_map.entrySet().iterator();
			if(iterator != null){
				while(iterator.hasNext()){
					Map.Entry<String,Long>nextEntry = iterator.next();
					if(nextEntry != null && currentTimeMillis - nextEntry.getValue() >= 300000){
						logger.info("clean limit IP"+ nextEntry.getValue());
						iterator.remove();
					}
				}
			}
		}
		return ;
	}
	
	//获取被限制访问的IP
	public static Map<String,Long>getLimited_ip_map(){
		return limit_ip_map;
	}

}

当请求发送到服务端时,增加一个过滤器用来对请求进行校验:

public class SessionFilter implements Filter{
	@Override
	public void init(FilterConfig filterConfig) throws ServletException{ }
	
	@Override
	public void doFilter(ServletRequest request , ServletResponse response, FilterChain chain)throws IOException, ServletException{
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		//获取请求的sessionId
		String sessionId = httpRequest.getSession().getId();
		//获取请求的IP地址
		String remoteAddress = httpRequest.getRemoteAddr();
		//调用SessionBlastManager的方法,判断请求的session是否存在攻击行为
		if(SessionBlastManager.checkSessionBlastAndLimitIp(remoteAddress,sessionId)){
			//如果此IP已经被限制则拒绝请求,返回至登录界面
			httpResponse.sendRedirect("/login");
			return;
		}
		//如果没有被限制 则继续执行
		......
		chain.doFilter(request,response);
	}
	
	@Override
	public void destory(){ }
}

接下来需要一个定时任务来不断清除失效的记录信息

@Configuration
@EnableScheduling
public class SessionBlastTimer extends TimerTask{
	@Scheduled(fixedRate = 1000L)
	@Override
	public void run(){
		SessionBlastManager.deleteTimeoutErrorRequest();
	}
}
//此定时任务的作用是每秒钟清理一次两分钟前的请求信息

最后还需要一个定时任务来清除被限制的IP信息

@Configuration
@EnableScheduling
public class SessionTime extends TimerTask{
	@Scheduled(fixedRate = 60000L,initialDelay=60000L)
	@Override
	public void run(){
		//清除被限制的IP地址
		SessionBlastManager.unlimitIp();
	}
}
//此定时任务的作用是每分钟清理一次被限制时间超过或等于5分钟的IP地址,使此IP具有访问权限

此功能最终是由一个过滤器和两个定时任务的组合完成目标功能。

总体的思路:
首先客户端发送请求给服务端,服务端过滤器会分析其session和IP是否匹配,(在核心业务类中出现过 sessionId_loginInfo_map 这个MAP实际上是记录了正常登陆的用户信息),通过此map可以检验出Session和IP是否正确。 如果session与IP不匹配则会对此IP进行记录,如果在两分钟内此IP发送的请求session信息错误超过60次,则认为此IP存在攻击的意图(判断是否存在攻击意图的标准自行确定,频率可以是100次或更多,时间可以是5分钟,10分钟)。当此IP被判断为存在攻击意图后将此IP信息添加进limited_ip_map中,下一次请求时会先判断请求的IP是否存在于limited_ip_map中,如果存在则说明此IP被限制,则拒绝请求,IP被限制5分钟后由定时任务自动解除对其的限制,恢复其请求权限。

猜你喜欢

转载自blog.csdn.net/weixin_43141627/article/details/85267350
今日推荐