前置内容:什么是Session爆破 (请参考 http://www.cnblogs.com/phpstudy2015-6/p/6776919.html )
session爆破的基本流程如下图所示:
本文将解决攻击者在第3步中使用暴力破解的方式获取合法的session:
实现思路:
攻击者通过携带不同的session向服务端发送请求以达到获取合法回话。因此可以为服务器端增加一个过滤器来判断某一IP是否存在潜在的攻击行为。
- 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分钟后由定时任务自动解除对其的限制,恢复其请求权限。