2009-05-04 3 views
16

Sto prendendo in considerazione lo sviluppo di un'app per Google App Engine, che non dovrebbe ottenere troppo traffico. Preferirei non pagare per superare le quote gratuite. Tuttavia, sembra che sarebbe abbastanza facile causare un attacco denial of service sovraccaricando l'app e superando le quote. Esistono metodi per prevenire o rendere più difficile il superamento delle quote gratuite? So che potrei, ad esempio, limitare il numero di richieste da un IP (rendendo più difficile superare la quota della CPU), ma esiste un modo per rendere più difficile superare le richieste o le quote di larghezza di banda?È possibile impedire DoSing su Google App Engine?

risposta

16

Non ci sono strumenti integrati per prevenire DoS. Se stai scrivendo Google Apps utilizzando java, puoi utilizzare il filtro service.FloodFilter. La seguente parte di codice verrà eseguita prima di qualsiasi tuo servlet.

package service; 

import java.io.IOException; 
import java.util.HashMap; 
import java.util.Map; 

import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletContext; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletRequest; 


/** 
* 
* This filter can protect web server from simple DoS attacks 
* via request flooding. 
* 
* It can limit a number of simultaneously processing requests 
* from one ip and requests to one page. 
* 
* To use filter add this lines to your web.xml file in a <web-app> section. 
* 
    <filter> 
     <filter-name>FloodFilter</filter-name> 
     <filter-class>service.FloodFilter</filter-class> 
     <init-param> 
      <param-name>maxPageRequests</param-name> 
      <param-value>50</param-value> 
     </init-param> 
     <init-param> 
      <param-name>maxClientRequests</param-name> 
      <param-value>5</param-value> 
     </init-param> 
     <init-param> 
      <param-name>busyPage</param-name> 
      <param-value>/busy.html</param-value> 
     </init-param> 
    </filter> 

    <filter-mapping> 
     <filter-name>JSP flood filter</filter-name> 
     <url-pattern>*.jsp</url-pattern> 
    </filter-mapping> 
* 
* PARAMETERS 
* 
* maxPageRequests: limits simultaneous requests to every page 
* maxClientRequests: limits simultaneous requests from one client (ip) 
* busyPage:   busy page to send to client if the limit is exceeded 
*      this page MUST NOT be intercepted by this filter 
* 
*/ 
public class FloodFilter implements Filter 
{ 
    private Map <String, Integer> pageRequests; 
    private Map <String, Integer> clientRequests; 

    private ServletContext context; 
    private int maxPageRequests = 50; 
    private int maxClientRequests = 10; 
    private String busyPage = "/busy.html"; 


    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    { 
     String page = null; 
     String ip = null; 

     try { 
      if (request instanceof HttpServletRequest) { 
       // obtaining client ip and page URI without parameters & jsessionid 
       HttpServletRequest req = (HttpServletRequest) request; 
       page = req.getRequestURI(); 

       if (page.indexOf(';') >= 0) 
        page = page.substring(0, page.indexOf(';')); 

       ip = req.getRemoteAddr(); 

       // trying & registering request 
       if (!tryRequest(page, ip)) { 
        // too many requests in process (from one client or for this page) 
        context.log("Flood denied from "+ip+" on page "+page); 
        page = null; 
        // forwarding to busy page 
        context.getRequestDispatcher(busyPage).forward(request, response); 
        return; 
       } 
      } 

      // requesting next filter or servlet 
      chain.doFilter(request, response); 
     } finally { 
      if (page != null) 
       // unregistering the request 
       releaseRequest(page, ip); 
     } 
    } 


    private synchronized boolean tryRequest(String page, String ip) 
    { 
     // checking page requests 
     Integer pNum = pageRequests.get(page); 

     if (pNum == null) 
      pNum = 1; 
     else { 
      if (pNum > maxPageRequests) 
       return false; 

      pNum = pNum + 1; 
     } 

     // checking client requests 
     Integer cNum = clientRequests.get(ip); 

     if (cNum == null) 
      cNum = 1; 
     else { 
      if (cNum > maxClientRequests) 
       return false; 

      cNum = cNum + 1; 
     } 

     pageRequests.put(page, pNum); 
     clientRequests.put(ip, cNum); 

     return true; 
    } 


    private synchronized void releaseRequest(String page, String ip) 
    { 
     // removing page request 
     Integer pNum = pageRequests.get(page); 

     if (pNum == null) return; 

     if (pNum <= 1) 
      pageRequests.remove(page); 
     else 
      pageRequests.put(page, pNum-1); 

     // removing client request 
     Integer cNum = clientRequests.get(ip); 

     if (cNum == null) return; 

     if (cNum <= 1) 
      clientRequests.remove(ip); 
     else 
      clientRequests.put(ip, cNum-1); 
    } 


    public synchronized void init(FilterConfig config) throws ServletException 
    { 
     // configuring filter 
     this.context = config.getServletContext(); 
     pageRequests = new HashMap <String,Integer>(); 
     clientRequests = new HashMap <String,Integer>(); 
     String s = config.getInitParameter("maxPageRequests"); 

     if (s != null) 
      maxPageRequests = Integer.parseInt(s); 

     s = config.getInitParameter("maxClientRequests"); 

     if (s != null) 
      maxClientRequests = Integer.parseInt(s); 

     s = config.getInitParameter("busyPage"); 

     if (s != null) 
      busyPage = s; 
    } 


    public synchronized void destroy() 
    { 
     pageRequests.clear(); 
     clientRequests.clear(); 
    } 
} 

Se si utilizza python, allora si può avere a rotolare il proprio filtro.

+0

Probabilmente userò Java per la velocità extra, quindi questo potrebbe aiutare. – Zifre

+0

App Engine ha avuto il supporto per i filtri DoS da un po 'di tempo. –

+3

Il filtro DOS funziona solo per IP noto? Non può gestire gli attacchi DDOS che non sono noti all'IP prima dell'avvio dell'attacco. Inoltre, l'esempio precedente non può proteggere l'utilizzo della larghezza di banda delle risorse statiche –

7

Non sono sicuro che sia possibile, ma lo App Engine FAQs indica che se è possibile mostrare che si tratta di un attacco DOS, rimborserà le commissioni associate all'attacco.

+0

Grazie ... se dovessi pagare per questo, mi sentirei molto meglio riguardo a questo problema. – Zifre

+3

A meno che tu non attivi la fatturazione, il superamento delle quote gratuite porterà il tuo sito offline per un breve periodo (molto meno di un giorno intero). Ti verrà addebitato solo se lo hai abilitato esplicitamente e potrai impostare il tuo limite di fatturazione. –

+3

Ho avuto un attacco DOS su un download di file statici (qualcosa come 20 MB x 600 volte in 2 ore) da un singolo IP, ho chiesto il rimborso e si sono rifiutati, dicendo che questo non è considerato come un attacco DOS. E dicono che se il servizio si è interrotto a causa del raggiungimento del budget giornaliero impostato, questo non viene considerato come "rifiuto". Direi che è meglio inventare il nostro modo di proteggere dagli attacchi DOS fino a quando Google non risolverà il problema. –

2

Sembra che abbiano un filtro basato su indirizzo IP disponibile sia per Python sia per Java ora (so che questo è un thread precedente, ma risulta ancora elevato in una ricerca su Google).

https://developers.google.com/appengine/docs/python/config/dos

+3

Questo non è utile in alcun attacco DDoS, il numero di utenti è molto maggiore dei 1000 IP che è possibile bloccare con quello strumento. Per non parlare del fatto che devi caricare nuovamente il tuo sito web ogni pochi minuti mentre i nuovi aggressori vengono coinvolti nell'attacco. –

1

E 'sempre possibile utilizzare un servizio che fornisce Denial of funzioni di protezione di servizio di fronte a un'applicazione App Engine. Ad esempio, Cloudflare fornisce un servizio ben rispettato https://www.cloudflare.com/waf/ e ce ne sono altri. La mia comprensione (disclaimer: non ho usato personalmente il servizio) che queste funzionalità sono disponibili sul piano gratuito.

È anche abbastanza facile costruire una frequenza basata su memcache che limita l'implementazione nell'applicazione stessa. Ecco il primo colpo che ho ricevuto da una ricerca su google per questo metodo: http://blog.simonwillison.net/post/57956846132/ratelimitcache. Questo meccanismo è valido e può essere conveniente dal momento che l'utilizzo condiviso di memcache può essere sufficiente ed è gratuito. Inoltre, andando su questa rotta ti mette in controllo delle manopole. Lo svantaggio è che l'applicazione stessa deve gestire la richiesta HTTP e decidere se autorizzarla o negarla, quindi potrebbe esserci un costo (o esaurimento della quota [gratuito]) da gestire.

Full Disclosure: lavoro su Google su App Engine e non ho alcuna associazione con Cloudflare o Simon Willison.

1

Il GAE firewall è stato rilasciato di recente, destinato a sostituire il precedente, piuttosto limitato, DoS Protection Service.

Supporta gli aggiornamenti programmatici delle regole del firewall tramite l'API di amministrazione (REST): apps.firewall.ingressRules che può essere combinata con una logica in-app per il rilevamento DoS come descritto in altre risposte. La differenza consiste nel fatto che una volta implementata la regola, le richieste incriminate non verranno più addebitate in quanto non raggiungono più l'app, quindi il filtro in-app non è necessario.