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

Jam*_*mes 25 java rest web-services jax-rs ddos

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

坏请求来自随机IP,所以我不能只禁止他们的IP(除非我动态地做,但我还没有考虑过).

目前,当我检测到已经进行了10次不良应用ID尝试的客户端时,我将它们放在我的应用中的阻止列表中,并拒绝当天该IP的进一步请求.

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

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

我的服务就像,

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

@GET
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
@Path("/my-request")
public String myRequest(String type,
    @Context HttpServletRequest requestContext,
    @Context HttpServletResponse response) {
...
}
Run Code Online (Sandbox Code Playgroud)

请参阅: 如何阻止对Web API的黑客攻击/ DOS攻击

Raf*_*ter 9

您正在寻找JAX-RS支持的异步响应.在泽西岛教程包含有关如何实现对请求的异步响应的一些例子.

使用异步响应,在请求完成之前,已释放响应请求的可解决的线程,以处理另一个请求.通过在@Suspended注释中添加参数来激活此功能.您还需要做的是注册一个专用的调度程序,负责在给定的超时后唤醒您的请求,如下例所示:

@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);
  }
}
Run Code Online (Sandbox Code Playgroud)

这样,在5秒的等待时间内没有阻塞线程,这为同时处理其他请求提供了机会.

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

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


sib*_*ick 5

我可能建议将 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

It is cheaper for application server: no XML/JSON deserialization, no call to REST classes. Also you may notice that I never call asyncContext.start. I check Wildfly 8.2 server. In this case Wildfly does not use thread per request. I sent a lot of requests, but amount of threads was constant.

PS

trying to guess application IDs by looping over random IDs

It is not DOS attack. It is brute force attack.


小智 4

有很多可能的解决方案,根据您给出的限制,我想到了两种可能的解决方案:

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

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