2015-09-30 17 views
24

Ho riscontrato un problema con un servizio Web con gli utenti che tentano di indovinare gli ID delle applicazioni eseguendo il loop su ID casuali.Come lasciare il client in attesa del servizio Java JAX-RS per prevenire il DOS

Le cattive richieste provengono da IP casuali, quindi non posso semplicemente escludere il loro IP (a meno che non lo faccia dinamicamente, ma non lo sto ancora esaminando).

Attualmente quando rilevo un client che ha effettuato 10 tentativi di ID app non validi li ho inseriti in un elenco di blocchi nella mia app e ho rifiutato ulteriori richieste da quell'IP per quel giorno.

Voglio ridurre al minimo la quantità di lavoro che il mio server deve fare, poiché il client cattivo continuerà a inviare migliaia di richieste anche se vengono rifiutate. So che ci sono soluzioni firewall dinamiche, ma per ora voglio qualcosa di facile da implementare nella mia app. Attualmente sto dormendo per 5 secondi per ridurre le chiamate, ma quello che voglio fare è semplicemente non inviare una risposta al client, quindi deve scadere.

Qualcuno sa come farlo in Java, in JAX-RS?

Il mio servizio è come,

@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

L'attaccante probabilmente non si preoccupa se i tempi di richiesta fuori. Sembra che provenga da una bot farm, il che significa che lasciare la connessione aperta per sempre è irrilevante. Non so di JAX RS, ma in Java guarderei il servlet asincrono (o le continuazioni) per non legare un thread del server che dorme per 5 secondi. Scusa, non so come farlo in JAXRS! – Jamie

+0

L'aggiunta del sonno riduce le chiamate, quindi lasciare che il timeout riduca ulteriormente le chiamate, penso che provenga da un'app Androd, non da una farm – James

+1

Hey @James hai provato a reindirizzare i client in un posto veramente lento o in un file enorme da qualche altra parte ? Se il client accetta il reindirizzamento, sarà occupato per un po 'e non avrai più bisogno di un thread dormiente. – konqi

risposta

4

Ci sono molte soluzioni possibili, con le richieste che hai dato 2 possibili soluzioni vengono in mente:

1) Utilizzare un proxy forward che abbia già il supporto per limitare le richieste. Personalmente ho usato Nginx e posso consigliarlo in parte perché è semplice da configurare. Config. Limitazione tasso rilevante: Limit Req Module

2) Utilizzare Asynchronous JAX-RS per sospendere il timeout della richiesta dannosa rilevata. La richiesta sensata può essere elaborata direttamente. Ma attenzione alle conseguenze, in entrambi i casi un tale approccio consumerà risorse sul server!

0

Non ho provato questo .... solo uno sparo nel buio qui, quindi prendilo con un granello di sale. Quindi il problema è che una volta rilevato qualcosa di sospetto e inserito l'IP in modalità di blocco, non si vuole sprecare un'altra frazione di risorse su questa richiesta e anche far perdere tempo al timeout. Ma il tuo framework risponderà se fai un'eccezione. Che ne dici di interrompere il thread corrente? È possibile farlo da Thread.currentThread().interrupt();. La speranza è che il contenitore Java che elabora la richiesta verificherà lo stato dell'interrupt. Potrebbe non farlo. So che ho visto le classi relative all'IO non elaborare le richieste perché è stato impostato il flag interrotto.

MODIFICA: se l'interruzione del thread non funziona, puoi anche provare a lanciare un InterruptedException. Potrebbe realizzare l'effetto desiderato.

0

Per quanto ne so, nelle specifiche Servlet non esiste un tale meccanismo. Siccome le implementazioni JAX-RS usano servlet (almeno Jersey e Resteasy), non hai un modo standard per farlo in Java.

L'idea di utilizzare il JAX-RS sincrono è migliore di Thread.sleep(5000), ma continuerà a utilizzare alcune risorse e non è altro che un modo per elaborare la richiesta in seguito, invece di ignorare la richiesta per sempre.

+0

Ecco un'altra risposta credibile: http://stackoverflow.com/questions/22668825/how-to-silently-drop-a-request-in-tomcat –

8

Si sta cercando asynchronous responses supportati da JAX-RS. tutorial for Jersey contiene alcuni esempi su come implementare una risposta asincrona a una richiesta.

Con risposte asincrone, il thread che è resposible per una risposta a una richiesta viene liberato per la gestione di un'altra richiesta già prima che una richiesta sia completata. Questa funzione è attivata aggiungendo un parametro con l'annotazione @Suspended. Quello che avrebbe bisogno di fare in più, sarebbe quella di registrare un apposito scheduler che è responsabile per il risveglio vostre richieste dopo un certo timeout come in questo esempio:

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

In questo modo, nessun thread è bloccato per il tempo di attesa di cinque secondi che dà l'opportunità di gestire altre richieste nel frattempo.

JAX-RS non offre un modo per scovare completamente una richiesta senza risposta. Sarà necessario mantenere la connessione aperta per produrre un timeout, se si interrompe la connessione, un utente non è interessato alla risoluzione. La cosa migliore che potresti fare sarebbe quella di non rispondere mai a una richiesta asincrona, ma questo continuerà a consumare risorse. Se si desidera evitare ciò, è necessario risolvere il problema al di fuori di JAX-RS, ad esempio inoltrando la richiesta da un altro server.

Un modo per farlo sarebbe set up mod_proxy in cui è possibile rispondere al proxy con un codice di errore per le richieste malleabili e impostare un limite di tentativi molto elevato per tali richieste.

+0

Questo è decisamente meglio di 'Thread.sleep (5000)' ma le risorse saranno essere ancora consumato Come ho capito la domanda, OP chiede di ignorare la richiesta di sempre, non tornando a elaborarla 5 secondi dopo. –

+0

In questo caso, è possibile non riprendere mai un respone asincrono ma sarebbe necessario mantenere la connessione aperta. In caso contrario, il cliente viene informato della cancellazione. Tuttavia, mantenere la connessione aperta consuma ancora alcune risorse. –

+0

Esattamente: non riprendere mai la risposta asincrona consumerà risorse del Sistema Operativo, in quanto la connessione rimane aperta. Quindi, non è una buona idea. OP vuole rilasciare silenziosamente la connessione e tutte le risorse correlate, lasciando che la connessione al client si interrompa. Anche se non si riprende la risposta asincrona, non si otterrà il comportamento desiderato. –

4

io suggerisco mossa IP negare la logica da resto a semplice filtro 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; 
    } 

} 

È più conveniente per application server: no deserializzazione XML/JSON, nessuna chiamata per riposare classi. Inoltre potresti notare che non chiamo mai asyncContext.start. Controllo il server Wildfly 8.2. In questo caso, Wildfly non utilizza thread per richiesta. Ho inviato molte richieste, ma la quantità di thread era costante.

PS

cercando di indovinare gli ID di applicazione da loop su ID casuali

Non è attacco DOS. È un attacco di forza bruta.

+0

_ "In questo caso, Wildfly non utilizza thread per richiesta.Ho inviato molte richieste, ma la quantità di thread è stata costante. "_ È perché non hai mai chiamato' asyncContext.start() '? – Keith

+0

Sì. Wildfly aggiunge le attività di scadenza alla coda e c'è un pool di thread per la coda di elaborazione , ma questo pool di thread assegna una quantità limitata di thread – sibnick

1

Puoi provare asyncContext supportato da Tomcat 3.0. Questa funzione disaccoppia il gestore e l'elaboratore delle richieste web. Nel tuo caso, il thread che accetta la richiesta deve attendere/dormire più del timeout configurato. Rendere lo stesso thread inattivo per così tanto tempo li farebbe morire di fame e ciò influirebbe drasticamente sulle prestazioni dei server. Quindi, l'elaborazione asincrona è la strada giusta da percorrere.

Ho usato asyncContext con l'esecutore Java a thread singolo http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html Ha funzionato bene per me. Avevo un caso aziendale simile, in cui dovevo prendere in giro la mia domanda.

consultare questo per l'attuazione http://peter-braun.org/2013/04/asynchronous-processing-from-servlet-3-0-to-jax-rs-2-0/

filo singolo esecutore non mangiare le risorse e la sua ideale per questo caso d'uso.

0

Una volta ho risolto un problema simile con creando un'applicazione tunnel TCP/IP.

È una piccola applicazione che ascolta una porta esterna (ad esempio la porta http 80).In condizioni normali, tutte le chiamate ricevute vengono accettate, creando oggetti socket dedicati. Questi singoli socket chiamano il server Web (nascosto) reale, che viene eseguito sullo stesso server fisico o su qualsiasi server all'interno della rete locale.

Il tunnel è come un dispatcher, ma può anche agire come un loadbalancer. Può essere multifunzionale.

Il fatto è che si sta lavorando a basso livello con prese a questo punto. Se si consegna un elenco di indirizzi IP vietati a questa applicazione, quindi è possibile chiudere le applicazioni su un livello molto basso (non accettando nemmeno le chiamate socket).

Si potrebbe integrare anche nella stessa applicazione java. Ma penso che sia più flessibile considerare questa come un'applicazione separata.

(Fatemi sapere se avete bisogno di qualche codice sorgente, io possa avere un certo codice, che in giro per iniziare)

+0

Mi rendo conto che questa risposta è un po 'inverosimile, e immagino che alcune persone possano voler votare meno, ma poiché nessun altro ha menzionato questo approccio, ... ho solo non volevo che trascurassi questa opzione, è solo un'idea open-minded open-the-box. :) Spero che tu non lo odi. – bvdb