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

2022-09-01 16:08:06

我在使用 Web 服务时遇到问题,用户试图通过循环访问随机 ID 来猜测应用程序 ID。

不良请求来自随机IP,所以我不能只是禁止他们的IP(除非我动态地这样做,但我还没有研究过)。

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

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

有人知道如何在Java中,在JAX-RS中做到这一点吗?

我的服务是这样的,

@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) {
...
}

请参见: 如何阻止对 Web API 的 hack/DOS 攻击


答案 1

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

使用异步响应,负责应答请求的线程将被释放,以便在请求完成之前处理另一个请求。此功能通过添加带有注释的参数来激活。您还需要做的是注册一个专用的调度程序,负责在给定超时后唤醒您的请求,如以下示例所示:@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);
  }
}

这样,在五秒钟的等待时间内,不会阻止任何线程,从而有机会在此期间处理其他请求。

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

一种方法是设置mod_proxy,您可以在其中为恶意请求提供错误代码来回答代理,并为此类请求设置非常大的重试限制。


答案 2

我可能会建议将 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类的调用。另外,您可能会注意到我从不打电话.我检查Wildfly 8.2服务器。在这种情况下,Wildfly 不会对每个请求使用线程。我发送了很多请求,但线程数量是恒定的。asyncContext.start

附言

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

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