设为首页 收藏本站
查看: 766|回复: 0

[经验分享] jetty防止Dos攻击的filter实现分析

[复制链接]

尚未签到

发表于 2017-2-27 10:18:03 | 显示全部楼层 |阅读模式
  jetty的org.eclipse.jetty.servlets.DoSFilter类是用来实现Dos攻击预防的filter,里面涉及到一些变量,先了解下变量的含义:
  

  protected long _delayMs;超过最大处理请求数当前请求的等待时间,-1立即拒绝,0,无限等待,正数表达等待的毫秒数
  protected long _throttleMs;异步等待获取信号量的时间
  protected long _maxWaitMs;阻塞等待获取信号量的时间
  protected long _maxRequestMs;请求处理最大时间限制
  protected long _maxIdleTrackerMs;跟踪连接是否断开的最大等待时间
  protected int _throttledRequests;允许在等待队列中等待获取信号量的请求数
  protected int _maxRequestsPerSec; 每秒允许处理最多的请求数,超过将延迟,异步等待。
  

  protected boolean _insertHeaders; 是否往response写入dosfilter信息,默认true
  protected boolean _trackSessions;是否根据session来检测dos攻击,默认true
  protected boolean _remotePort;是否根据ip+port来检测dos攻击,默认false
  protected String _whitelistStr;  白名单 ip白名单列表,这些通过都通过配置servlet的init-p
  aram可以来重新设置。
  首先看看init方法的初始化设置:
  


public void init(FilterConfig filterConfig)
{
_context = filterConfig.getServletContext();
_queue = new Queue[getMaxPriority() + 1];
_listener = new ContinuationListener[getMaxPriority() + 1];
for (int p = 0; p < _queue.length; p++)
{
_queue[p] = new ConcurrentLinkedQueue<Continuation>();
final int priority=p;
_listener[p] = new ContinuationListener()
{
public void onComplete(Continuation continuation)
{
}
public void onTimeout(Continuation continuation)
{
_queue[priority].remove(continuation);
}
};
}
_rateTrackers.clear();
int baseRateLimit = __DEFAULT_MAX_REQUESTS_PER_SEC;
if (filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM) != null)
baseRateLimit = Integer.parseInt(filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM));
_maxRequestsPerSec = baseRateLimit;
long delay = __DEFAULT_DELAY_MS;
if (filterConfig.getInitParameter(DELAY_MS_INIT_PARAM) != null)
delay = Integer.parseInt(filterConfig.getInitParameter(DELAY_MS_INIT_PARAM));
_delayMs = delay;
int throttledRequests = __DEFAULT_THROTTLE;
if (filterConfig.getInitParameter(THROTTLED_REQUESTS_INIT_PARAM) != null)
throttledRequests = Integer.parseInt(filterConfig.getInitParameter(THROTTLED_REQUESTS_INIT_PARAM));
_passes = new Semaphore(throttledRequests,true);
_throttledRequests = throttledRequests;
long wait = __DEFAULT_WAIT_MS;
if (filterConfig.getInitParameter(MAX_WAIT_INIT_PARAM) != null)
wait = Integer.parseInt(filterConfig.getInitParameter(MAX_WAIT_INIT_PARAM));
_maxWaitMs = wait;
long suspend = __DEFAULT_THROTTLE_MS;
if (filterConfig.getInitParameter(THROTTLE_MS_INIT_PARAM) != null)
suspend = Integer.parseInt(filterConfig.getInitParameter(THROTTLE_MS_INIT_PARAM));
_throttleMs = suspend;
long maxRequestMs = __DEFAULT_MAX_REQUEST_MS_INIT_PARAM;
if (filterConfig.getInitParameter(MAX_REQUEST_MS_INIT_PARAM) != null )
maxRequestMs = Long.parseLong(filterConfig.getInitParameter(MAX_REQUEST_MS_INIT_PARAM));
_maxRequestMs = maxRequestMs;
long maxIdleTrackerMs = __DEFAULT_MAX_IDLE_TRACKER_MS_INIT_PARAM;
if (filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM) != null )
maxIdleTrackerMs = Long.parseLong(filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM));
_maxIdleTrackerMs = maxIdleTrackerMs;
_whitelistStr = "";
if (filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM) !=null )
_whitelistStr = filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM);
initWhitelist();
String tmp = filterConfig.getInitParameter(INSERT_HEADERS_INIT_PARAM);
_insertHeaders = tmp==null || Boolean.parseBoolean(tmp);
tmp = filterConfig.getInitParameter(TRACK_SESSIONS_INIT_PARAM);
_trackSessions = tmp==null || Boolean.parseBoolean(tmp);
tmp = filterConfig.getInitParameter(REMOTE_PORT_INIT_PARAM);
_remotePort = tmp!=null&& Boolean.parseBoolean(tmp);
_requestTimeoutQ.setNow();
_requestTimeoutQ.setDuration(_maxRequestMs);
_trackerTimeoutQ.setNow();
_trackerTimeoutQ.setDuration(_maxIdleTrackerMs);
_running=true;
_timerThread = (new Thread()
{
public void run()
{
try
{
while (_running)
{
long now;
synchronized (_requestTimeoutQ)
{
now = _requestTimeoutQ.setNow();
_requestTimeoutQ.tick();
}
synchronized (_trackerTimeoutQ)
{
_trackerTimeoutQ.setNow(now);
_trackerTimeoutQ.tick();
}
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
Log.ignore(e);
}
}
}
finally
{
Log.info("DoSFilter timer exited");
}
}
});
_timerThread.start();
if (_context!=null && Boolean.parseBoolean(filterConfig.getInitParameter(MANAGED_ATTR_INIT_PARAM)))
_context.setAttribute(filterConfig.getFilterName(),this);
}

  _queue是用来保存当前当前通过header,session,ip等检测类型的请求队列,然后就是一堆参数的初始化设置,最后
  又有两个queue _requestTimeoutQ是来保存每个请求的处理时间超时检测的队列;_trackerTimeoutQ是来检测请求对应连接是否已经关闭超时的检测队列;最后启动一个_timerThread线程来进行这两个队列的超时检测。
  然后再看下dofilter的处理
  


  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException
{
final HttpServletRequest srequest = (HttpServletRequest)request;
final HttpServletResponse sresponse = (HttpServletResponse)response;
final long now=_requestTimeoutQ.getNow();
// Look for the rate tracker for this request
RateTracker tracker = (RateTracker)request.getAttribute(__TRACKER);
if (tracker==null) //如果request没有进行过dosfilter的处理
{
// This is the first time we have seen this request.
// get a rate tracker associated with this request, and record one hit
tracker = getRateTracker(request); //根据request生成dos跟踪的对象,看下面方法
// Calculate the rate and check it is over the allowed limit
final boolean overRateLimit = tracker.isRateExceeded(now);//判断是否已经超过每秒最大处理请求数
// pass it through if  we are not currently over the rate limit
if (!overRateLimit)//如果没有超过,则正常处理
{
doFilterChain(filterchain,srequest,sresponse);
return;
}
// We are over the limit.
Log.warn("DOS ALERT: ip="+srequest.getRemoteAddr()+",session="+srequest.getRequestedSessionId()+",user="+srequest.getUserPrincipal());
// So either reject it, delay it or throttle it
switch((int)_delayMs) //根据当前配置的延时时间
{
case -1: //如果为-1,则直接拒绝
{
// Reject this request
if (_insertHeaders) //是否把dosfilter处理插入response的header
((HttpServletResponse)response).addHeader("DoSFilter","unavailable");
((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return;
}
case 0: //如果为0,则当前线程继续等待处理,
{
// fall through to throttle code
request.setAttribute(__TRACKER,tracker);
break;
}
default://其他字段,则设置request的timeout时间为_delayMs,并且挂起当前线程,返回
{
// insert a delay before throttling the request
if (_insertHeaders)
((HttpServletResponse)response).addHeader("DoSFilter","delayed");
Continuation continuation = ContinuationSupport.getContinuation(request);
request.setAttribute(__TRACKER,tracker);
if (_delayMs > 0)
continuation.setTimeout(_delayMs);
continuation.suspend();
return;
}
}
}
//_delayMs为0是,当前请求继续等待
// Throttle the request
boolean accepted = false;
try
{
// check if we can afford to accept another request at this time
accepted = _passes.tryAcquire(_maxWaitMs,TimeUnit.MILLISECONDS);//判断当前处理请求队列是否已经有处理完的请求,处理完的会释放信号量,则当前请求线程可以获取信号量
if (!accepted) //如果不能获取,则把当前请求设置为异步等待,异步等待的时间为_throttleMs,
{
// we were not accepted, so either we suspend to wait,or if we were woken up we insist or we fail
final Continuation continuation = ContinuationSupport.getContinuation(request);
Boolean throttled = (Boolean)request.getAttribute(__THROTTLED);
if (throttled!=Boolean.TRUE && _throttleMs>0)
{
int priority = getPriority(request,tracker);
request.setAttribute(__THROTTLED,Boolean.TRUE);
if (_insertHeaders)
((HttpServletResponse)response).addHeader("DoSFilter","throttled");
if (_throttleMs > 0)
continuation.setTimeout(_throttleMs);
continuation.suspend();
continuation.addContinuationListener(_listener[priority]);
_queue[priority].add(continuation);
return;
}
// else were we resumed?
else if  (request.getAttribute("javax.servlet.resumed")==Boolean.TRUE)
{//如果线程中心被唤醒,则可以获取到信号量,
// we were resumed and somebody stole our pass, so we wait for the next one.
_passes.acquire();
accepted = true;
}
}
// if we were accepted (either immediately or after throttle)
if (accepted) //获取到了,继续执行,
// call the chain
doFilterChain(filterchain,srequest,sresponse);
else
{
// fail the request
if (_insertHeaders)
((HttpServletResponse)response).addHeader("DoSFilter","unavailable");
((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}
catch (InterruptedException e)
{
_context.log("DoS",e);
((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
finally
{
if (accepted) //执行完了之后释放当前的信号量,唤醒等待队列中的第一个请求进行处理
{
// wake up the next highest priority request.
for (int p = _queue.length; p-- > 0;)
{
Continuation continuation = _queue[p].poll();
if (continuation != null && continuation.isSuspended())
{
continuation.resume();
break;
}
}
_passes.release();
}
}
}
public RateTracker getRateTracker(ServletRequest request)
{
HttpServletRequest srequest = (HttpServletRequest)request;
HttpSession session=srequest.getSession(false);
String loadId = extractUserId(request);
final int type;
if (loadId != null)
{
type = USER_AUTH;
}
else
{
if (_trackSessions && session!=null && !session.isNew())
{
loadId=session.getId();
type = USER_SESSION;
}
else
{
loadId = _remotePort?(request.getRemoteAddr()+request.getRemotePort()):request.getRemoteAddr();
type = USER_IP;
}
}
RateTracker tracker=_rateTrackers.get(loadId);
if (tracker==null)
{
RateTracker t;
if (_whitelist.contains(request.getRemoteAddr())) //如果在白名单中,则isRateExceeded一直返回false
{
t = new FixedRateTracker(loadId,type,_maxRequestsPerSec);
}
else
{
t = new RateTracker(loadId,type,_maxRequestsPerSec);
}
tracker=_rateTrackers.putIfAbsent(loadId,t);
if (tracker==null)
tracker=t;
if (type == USER_IP)
{
// USER_IP expiration from _rateTrackers is handled by the _trackerTimeoutQ
synchronized (_trackerTimeoutQ)
{
_trackerTimeoutQ.schedule(tracker);
}
}
else if (session!=null)
// USER_SESSION expiration from _rateTrackers are handled by the HttpSessionBindingListener
session.setAttribute(__TRACKER,tracker);
}
return tracker;
}
 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347804-1-1.html 上篇帖子: 解决Maven中Jetty插件在控制台乱码的问题 下篇帖子: 解决访问Jetty页面中JSP时的java.err.nojdk
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表