我遇到了一个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>
答案 0 :(得分:8)
您正在寻找JAX-RS支持的asynchronous responses。 tutorial 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应用程序中。但我认为将其视为一个单独的应用程序会更灵活。
(如果您需要一些源代码,请告诉我,我可能会提供一些代码来帮助您入门)