如何让客户端等待Java JAX-RS服务来阻止DOS

时间:2015-09-30 19:40:31

标签: java web-services rest jax-rs ddos

我遇到了一个Web服务的问题,用户试图通过循环随机ID来猜测应用程序ID。

错误的请求来自随机IP,因此我不能只禁止他们的IP(除非我动态地执行,但我还没有调查它)。

目前,当我检测到有10个不良应用ID尝试的客户端时,我将它们放在我的应用中的阻止列表中,并在当天拒绝来自该IP的进一步请求。

我想最大限度地减少我的服务器需要做的工作量,因为坏客户端将继续发送1000个请求,即使它们被拒绝。我知道有动态防火墙解决方案,但现在想要在我的应用程序中轻松实现。目前我正在睡觉5秒钟以减少通话,但我想做的只是不向客户端发送响应,所以它必须超时。

任何人都知道如何在JAX-RS中用Java做到这一点?

我的服务就像,

<div id="map-container">
  <div id="map-canvas"></div>
</div>

<script>
$(document).ready(function(){
  handler = Gmaps.build('Google');
  handler.buildMap({ provider: {}, internal: {id: 'map-canvas'}}, function(){
    markers = handler.addMarkers(<%=raw @hash.to_json %>)
    handler.bounds.extendWith(markers);
    handler.fitMapToBounds(markers);
    if(navigator.geolocation)
      navigator.geolocation.getCurrentPosition;
  });
});
</script>

请参阅: How to stop hack/DOS attack on web API

7 个答案:

答案 0 :(得分:8)

您正在寻找JAX-RS支持的asynchronous responsestutorial for Jersey包含一些如何实现对请求的异步响应的示例。

使用异步响应,负责回答请求的线程在请求​​完成之前已经处理了另一个请求。通过添加带@Suspended注释的参数来激活此功能。另外,您需要注册一个专用的scheduler,负责在给定的超时后唤醒您的请求,如下例所示:

@Path("/api")
public class MyServer {

  private ScheduledExecutorService scheduler = ...;

  @GET
  @Consumes(MediaType.APPLICATION_XML)
  @Produces(MediaType.APPLICATION_XML)
  @Path("/my-request")
  public String myRequest(String type,
                          @Context HttpServletRequest requestContext,
                          @Context HttpServletResponse response,
                          @Suspended AsyncResponse asyncResponse) {
    scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        asyncResponse.resume(...)
      }
    }, 5, TimeUnit.SECOND);
  }
}

这样,在5秒的等待时间内没有阻塞任何线程,这给了机会在此期间处理其他请求。

JAX-RS没有提供在没有答案的情况下完全丢弃请求的方法。您需要保持连接打开以产生超时,如果终止连接,则会通知用户终止。你能做的最好的事情就是永远不会回答异步请求,但这仍会消耗一些资源。如果你想避免这种情况,你必须解决JAX-RS之外的问题,例如通过代理另一台服务器的请求。

执行此操作的一种方法是set up mod_proxy,您可以使用错误代码回答代理,并为此类请求设置非常大的重试限制。

答案 1 :(得分:4)

有许多可能的解决方案,您已经给出了限制,我想到了两种可能的解决方案:

1)使用已经支持限制请求的转发代理。我个人使用Nginx并且可以推荐它,部分原因是设置起来很简单。相关的速率限制配置:Limit Req Module

2)使用Asynchronous JAX-RS让您检测到的恶意请求超时。可以直接处理Sane请求。但要注意后果,这种方法会消耗服务器上的资源!

答案 2 :(得分:4)

我可能会建议将IP拒绝逻辑从REST迁移到普通HTTP过滤器:

@WebFilter(urlPatterns = "/*", asyncSupported = true)
@WebListener
public class HttpFilter implements Filter {

    @Override
   public void init(FilterConfig filterConfig) throws ServletException {   }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if(denyIP(servletRequest.getRemoteAddr())) {
            AsyncContext asyncContext = servletRequest.startAsync();
            asyncContext.setTimeout(100000);
        }else{
           filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {   }

    private boolean denyIP(String address){
         //todo
         return true;
    }

}

应用程序服务器更便宜:没有XML / JSON反序列化,也没有调用REST类。你也可能注意到我从不打电话给asyncContext.start。我检查Wildfly 8.2服务器。在这种情况下,Wildfly不会根据请求使用线程。我发送了很多请求,但线程数量不变。

PS

  

尝试通过循环随机ID来猜测应用程序ID

这不是DOS攻击。这是暴力攻击。

答案 3 :(得分:1)

您可以尝试从Tomcat 3.0支持的asyncContext。 此功能将Web请求处理程序和处理程序分离。在您的情况下,接受请求的线程必须等待/休眠超过配置的超时。让相同的线程睡眠这么长时间会使他们饿死,这会严重影响服务器的性能。因此,异步处理是正确的方法。

我在Java单线程执行器http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html中使用了asyncContext 它对我很有用。我有类似的商业案例,我不得不模仿我的申请。

请参阅此实施http://peter-braun.org/2013/04/asynchronous-processing-from-servlet-3-0-to-jax-rs-2-0/

单线程执行程序不会占用资源,也不适合这个用例。

答案 4 :(得分:0)

我没有尝试过这个......只是在黑暗中拍摄,所以带上一粒盐。所以问题在于,一旦你发现了一些可疑的东西并将IP置于块模式,你就不希望在这个请求上浪费另外的资源,并且还会浪费时间超时。但是如果抛出异常,您的框架将会响应。打断你当前的线程怎么样?您可以Thread.currentThread().interrupt();执行此操作。希望处理请求的Java容器将检查中断状态。它可能不会这样做。我知道我已经看到IO相关类没有处理请求,因为设置了中断标志。

编辑:如果中断你的线程不起作用,你也可以尝试抛出InterruptedException。它可能会达到预期的效果。

答案 5 :(得分:0)

据我所知,在Servlet规范中没有这样的机制。由于JAX-RS实现使用servlet(至少是Jersey和Resteasy),因此没有一种标准方法可以在Java中实现这一点。

使用异步JAX-RS的想法比Thread.sleep(5000)更好,但它仍然会使用一些资源,只是一种处理请求的方法,而不是忽略总是请求。

答案 6 :(得分:0)

我曾经通过创建TCP / IP隧道应用程序解决了类似的问题。

这是一个小型应用程序侦听外部端口(例如http端口80)。在正常情况下,接受所有接收的呼叫,创建专用套接字对象。 这些单个套接字然后调用真实(隐藏)网络服务器,它在同一物理服务器或本地网络中的任何服务器上运行。

隧道本身就像调度员一样,但也可以像负载均衡器一样。它可以是多功能的。

此时您正在使用套接字进行低级操作。如果你将禁止的ip-address列表交给这个应用程序,那么它可以关闭非常低级别的应用程序(甚至根本不接受他们的套接字调用)。

您也可以将它集成到同一个Java应用程序中。但我认为将其视为一个单独的应用程序会更灵活。

(如果您需要一些源代码,请告诉我,我可能会提供一些代码来帮助您入门)