2015-09-30 13 views
24

Ich habe ein Problem mit einem Webdienst mit Benutzern, die versuchen, Anwendungs-IDs durch Schleifen über zufällige IDs zu erraten.Wie Client auf Java JAX-RS-Dienst warten, um DOS zu verhindern

Die schlechten Anfragen kommen von zufälligen IPs, also kann ich ihre IP nicht einfach verbieten (außer ich mache es dynamisch, aber ich schaue das noch nicht an).

Derzeit, wenn ich einen Client, der 10 schlechte App-ID-Versuche gemacht hat, entdecke ich sie in eine Sperrliste in meiner App, und lehnen weitere Anfragen von dieser IP für den Tag.

Ich möchte den Arbeitsaufwand meines Servers minimieren, da der fehlerhafte Client weiterhin Tausende von Anfragen sendet, obwohl sie abgelehnt werden. Ich weiß, dass es dynamische Firewall-Lösungen gibt, möchte aber vorerst etwas in meiner App implementieren. Derzeit schlafe ich für 5 Sekunden, um die Anrufe zu reduzieren, aber was ich tun möchte, ist einfach keine Antwort an den Client zu senden, so dass es eine Zeitüberschreitung hat.

Wer weiß, wie man das in Java macht, in JAX-RS?

Mein Service ist wie,

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

See: How to stop hack/DOS attack on web API

+2

Der Angreifer wahrscheinlich kümmert sich nicht darum, ob die Anforderung mal aus. Es hört sich an, als käme es von einer Bot-Farm, was bedeutet, dass die Verbindung für immer offen bleiben muss. Ich weiß nicht über JAX RS, aber in Java würde ich asynchrone Servlet (oder Fortsetzungen) betrachten, um keinen Server-Thread zu binden, der für 5 Sekunden schläft. Tut mir leid, ich weiß nicht, wie man das in JAXRS macht! – Jamie

+0

Das Hinzufügen des Schlafes reduziert die Anrufe, so dass die Zeitüberschreitung Anrufe weiter reduzieren sollte. Ich denke, es kommt von einer Androd-App, nicht von einer Farm. – James

+1

Hey @James, du hast versucht, die Clients irgendwo wirklich langsam oder eine riesige Datei umzuleiten ? Wenn der Client die Umleitung akzeptiert, wird er für eine Weile besetzt sein und Sie brauchen keinen schlafenden Thread mehr. – konqi

Antwort

4

Es gibt viele mögliche Lösungen, mit den Einschränkungen, die Sie gegeben haben zwei mögliche Lösungen kommen mir in den Sinn:

1) Verwenden Sie einen Forward-Proxy, der bereits Anforderungen zur Einschränkung von Anforderungen unterstützt. Ich persönlich habe Nginx benutzt und kann es teilweise empfehlen, weil es einfach einzurichten ist. Relevante Rate limiting config: Limit Req Module

2) Verwenden Sie Asynchronous JAX-RS, um die bösartige Anfrage, die Sie entdeckt haben, abzubrechen. Sane Anfrage kann direkt bearbeitet werden. Aber hüte dich vor den Konsequenzen, egal wie ein solcher Ansatz Ressourcen auf dem Server verbraucht!

0

Ich habe das nicht versucht .... nur ein Schuss im Dunkeln hier, also nimm es mit einem Körnchen Salz. Das Problem ist also, dass Sie, sobald Sie etwas Faules entdeckt haben und die IP im Block-Modus haben, keine weiteren Ressourcen für diese Anfrage verschwenden wollen und sie auch Zeitverschwendung verschwenden. Aber Ihr Framework wird reagieren, wenn Sie eine Ausnahme auslösen. Wie wäre es, deinen aktuellen Thread zu unterbrechen? Sie können dies durch Thread.currentThread().interrupt(); tun. Die Hoffnung ist, dass der Java-Container, der die Anfrage verarbeitet, den Interrupt-Status überprüft. Vielleicht macht es das nicht. Ich weiß, dass IO-bezogene Klassen Anfragen nicht verarbeitet haben, weil das unterbrochene Flag gesetzt wurde.

EDIT: Wenn das Unterbrechen Ihres Threads nicht funktioniert, können Sie auch versuchen, eine InterruptedException zu werfen. Es könnte den gewünschten Effekt erzielen.

0

Soweit ich weiß, gibt es in der Servlet-Spezifikation keinen solchen Mechanismus. Da JAX-RS-Implementierungen Servlets (mindestens Jersey und Rasteasy) verwenden, gibt es keine Standardmethode, um dies in Java zu erreichen.

Die Idee der Verwendung der aynchronen JAX-RS ist besser als Thread.sleep(5000), aber es wird immer noch einige Ressourcen verwenden und ist nichts anderes als eine Möglichkeit, die Anfrage später zu verarbeiten, anstatt die Anfrage für immer zu ignorieren.

+0

Hier eine weitere glaubwürdige Antwort: http://stackoverflow.com/questions/22668825/how-to-silent-drop-a-request-in-tomcat –

8

Sie suchen nach asynchronous responses, die von JAX-RS unterstützt werden. Die tutorial for Jersey enthält einige Beispiele zum Implementieren einer asynchronen Antwort auf eine Anforderung.

Bei asynchronen Antworten wird der Thread, der für das Beantworten einer Anforderung verantwortlich ist, für die Verarbeitung einer anderen Anforderung freigegeben, bevor eine Anforderung abgeschlossen wird. Diese Funktion wird aktiviert, indem ein Parameter mit der @Suspended Annotation hinzugefügt wird. Was Sie zusätzlich tun müsste, wäre eine eigene scheduler zu registrieren, die für das Aufwachen Ihre Anfragen nach einer bestimmten Zeit aus wie in diesem Beispiel verantwortlich:

@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); 
    } 
} 

Auf diese Weise kein Gewinde für die blockiert Wartezeit von fünf Sekunden, was Gelegenheit bietet, andere Anfragen in der Zwischenzeit zu bearbeiten.

JAX-RS bietet keine Möglichkeit, eine Anfrage ohne Antwort vollständig zu klären. Sie müssen die Verbindung offen halten, um eine Auszeit zu erzeugen. Wenn Sie die Verbindung beenden, wird ein Benutzer über die Beendigung informiert. Das Beste, was Sie tun könnten, wäre, niemals eine asynchrone Anfrage zu beantworten, aber dies wird immer noch einige Ressourcen verbrauchen. Wenn Sie dies vermeiden möchten, müssen Sie das Problem außerhalb von JAX-RS lösen, indem Sie beispielsweise die Anforderung von einem anderen Server übernehmen.

Eine Möglichkeit dazu wäre set up mod_proxy, wo Sie den Proxy mit einem Fehlercode für die mallicious Anfragen beantworten und ein sehr großes Wiederholungslimit für solche Anforderungen einrichten können.

+0

Dies ist definitiv besser als 'Thread.sleep (5000)' aber Ressourcen werden noch konsumiert werden. Als ich die Frage verstanden habe, bittet OP darum, die Anfrage für immer zu ignorieren und nicht 5 Sekunden später zur Verarbeitung zurückzukehren. –

+0

In diesem Fall ist es möglich, niemals eine asynchrone Antwort fortzusetzen, aber es wäre notwendig, die Verbindung offen zu halten. Andernfalls wird der Kunde über die Stornierung informiert. Das Offenhalten der Verbindung verbraucht jedoch immer noch einige Ressourcen. –

+0

Genau: Nie wieder die asynchrone Antwort wird Ressourcen des Betriebssystems verbrauchen, wie die Verbindung geöffnet bleibt. Also keine gute Idee. OP möchte die Verbindung und alle damit verbundenen Ressourcen stilllegen und die Verbindung zum Client unterbrechen. Selbst wenn Sie die asynchrone Antwort nie fortsetzen, erhalten Sie nicht das gewünschte Verhalten. –

4

kann ich bewegen IP verweigern vorschlagen Logik von REST auf Plain-HTTP-Filter:

@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; 
    } 

} 

Es ist billiger für Anwendungsserver: keine XML/JSON Deserialisierung keine Anrufklassen zur Ruhe. Vielleicht bemerken Sie auch, dass ich nie asyncContext.start anrufe. Ich überprüfe Wildfly 8.2 Server. In diesem Fall verwendet Wildfly keinen Thread pro Anfrage. Ich habe viele Anfragen geschickt, aber die Anzahl der Threads war konstant.

PS

versuchen, Anwendungs-IDs zu erraten, durch zufällige IDs Schleifen über

Es ist nicht DoS-Angriff. Es ist ein Brute-Force-Angriff.

+0

_ "In diesem Fall verwendet Wildfly keinen Thread pro Anfrage.Ich habe viele Anfragen gesendet, aber die Anzahl der Threads war konstant. "_ Ist das, weil Sie' asyncContext.start() 'nie aufgerufen haben? – Keith

+0

Ja. Wildfly fügt der Warteschlange Ablauf-Tasks hinzu und es gibt einen Thread-Pool für die Verarbeitungswarteschlange , aber dieser Thread-Pool weist eine begrenzte Anzahl von Threads zu. – sibnick

1

Sie können asyncContext versuchen, der von Tomcat 3.0 unterstützt wird. Diese Funktion entkoppelt den Web-Request-Handler und den Prozessor. In Ihrem Fall muss der Thread, der die Anfrage akzeptiert, länger warten/schlafen als das konfigurierte Zeitlimit. Würde man den gleichen Thread für so lange Zeit schlafen lassen, würden sie verhungern und die Leistung der Server würde sich drastisch verschlechtern. Asynchrone Verarbeitung ist also der richtige Weg.

Ich habe asyncContext mit Java single thread executor verwendet http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html Es hat gut für mich gearbeitet. Ich hatte einen ähnlichen Geschäftsfall, wo ich meine Bewerbung verspotten musste.

Siehe dies für die Umsetzung http://peter-braun.org/2013/04/asynchronous-processing-from-servlet-3-0-to-jax-rs-2-0/

Einfaden Testamentsvollstrecker würde Ressourcen und ist ideal für diesen Anwendungsfall nicht essen.

0

Ich löste einmal ein ähnliches Problem durch Erstellen einer TCP/IP-Tunnel-Anwendung.

Es ist eine kleine Anwendung, die hört auf einen externen Port (z. B. der HTTP-Port 80).Unter normalen Bedingungen werden alle empfangenen Anrufe akzeptiert, wodurch dedizierte Socket-Objekte erstellt werden. Diese einzelnen Sockets rufen dann den echten (versteckten) Webserver auf, der auf demselben physischen Server oder auf einem beliebigen Server in Ihrem lokalen Netzwerk ausgeführt wird.

Der Tunnel selbst ist wie ein Dispatcher, kann aber wirken auch wie ein Loadbalancer. Es kann multifunktional sein.

Die Sache ist, dass Sie arbeiten Low-Level mit Sockets an dieser Stelle. Wenn Sie eine Liste von gesperrten IP-Adressen an diese Anwendung übergeben, dann kann Anwendungen auf einer sehr niedrigen Ebene (nicht sogar akzeptieren ihre Socket-Aufrufe überhaupt) ausschließen.

Sie könnten dies auch in der gleichen Java-Anwendung integrieren. Aber ich denke, es ist flexibler, dies als eine separate Anwendung zu betrachten.

(Lassen Sie mich wissen, wenn Sie etwas Quellcode benötigen, kann ich zur Festlegung einige Code haben um Ihnen den Einstieg)

+0

Mir ist klar, dass diese Antwort ein wenig weit hergeholt ist, und ich schätze, einige Leute wollen das vielleicht abstimmen. Aber da niemand diese Vorgehensweise erwähnt hat, ... ich einfach Ich wollte nicht, dass du diese Option übersiehst, es ist nur eine aufgeschlossene Idee von außen :) Ich hoffe, du hasst es nicht. – bvdb