2009-05-04 8 views
16

Estoy considerando desarrollar una aplicación para Google App Engine, que no debería recibir demasiado tráfico. Prefiero no pagar para exceder las cuotas gratuitas. Sin embargo, parece que sería bastante fácil provocar un ataque de denegación de servicio al sobrecargar la aplicación y exceder las cuotas. ¿Hay algún método para prevenir o dificultar la superación de las cuotas gratuitas? Sé que podría, por ejemplo, limitar el número de solicitudes de una IP (lo que hace más difícil exceder la cuota de la CPU), pero ¿hay alguna manera de hacer que sea más difícil exceder las solicitudes o las cuotas de ancho de banda?¿Es posible evitar DoSing en Google App Engine?

Respuesta

16

No hay herramientas integradas para evitar el DoS. Si está escribiendo Google Apps usando java, puede usar el filtro service.FloodFilter. La siguiente pieza de código se ejecutará antes que cualquiera de sus Servlets.

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

Si está utilizando Python, entonces puede que tenga que rodar su propio filtro.

+0

Probablemente voy a usar Java para la velocidad extra, por lo que esto podría ayudar. – Zifre

+0

App Engine cuenta con soporte para filtro DoS desde hace un tiempo. –

+3

El filtro DOS solo funciona para IP conocida ¿verdad? No puede tratar con ataques DDOS que IP no se conocen antes de que comience el ataque. Además, el ejemplo anterior no puede proteger el uso del ancho de banda de los recursos estáticos –

7

No estoy seguro de si es posible, pero el App Engine FAQs indica que si puede demostrar que es un ataque de DOS, entonces le reembolsarán las tarifas asociadas con el ataque.

+0

Gracias ... si tuviera que pagar, me haría sentir mucho mejor sobre este tema. – Zifre

+3

A menos que habilite la facturación, exceder las cuotas gratuitas simplemente desconectará su sitio por un período corto (mucho menos que un día entero). Solo se lo facturará si lo habilitó explícitamente y puede establecer su propio límite de facturación. –

+3

Experimenté un ataque de DOS en una descarga de archivos estáticos (algo así como 20 MB x 600 veces en 2 horas) desde una única IP, pedí un reembolso y se negaron, diciendo que esto no se considera un ataque de DOS. Y dicen que si el servicio se detuvo debido a que alcanzó el presupuesto diario que estableció, esto no se considera como "denegación". Yo diría que es mejor que inventemos nuestra propia forma de protegernos del ataque de DOS por ahora hasta que Google solucione su problema. –

2

Parece que tienen un filtro basado en la dirección IP disponible tanto para Python como para Java ahora (sé que este es un hilo viejo, pero todavía aparece en una posición alta en una búsqueda de Google).

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

+3

Esto no es útil en ningún ataque DDoS, el número de usuarios es mucho mayor que los 1000 IP que puede bloquear con esa herramienta. Sin mencionar que tienes que volver a cargar tu sitio web cada pocos minutos a medida que los nuevos atacantes se involucran en el ataque. –

1

Siempre es posible utilizar un servicio que proporciona características de protección de denegación de servicio en frente de una aplicación de App Engine. Por ejemplo, Cloudflare proporciona un servicio muy respetado https://www.cloudflare.com/waf/, y hay otros. Entiendo (descargo de responsabilidad: no he usado el servicio personalmente) que estas funciones están disponibles en el plan gratuito.

También es bastante fácil construir una implementación de límite de velocidad basada en Memcache en su aplicación. Este es el primer golpe que obtuve de una búsqueda en google para este método: http://blog.simonwillison.net/post/57956846132/ratelimitcache. Este mecanismo es sólido y puede ser rentable ya que el uso compartido de Memcache puede ser suficiente y gratuito. Además, seguir esta ruta te permite controlar las perillas. El inconveniente es que la aplicación en sí debe manejar la solicitud HTTP y decidir si la permite o la niega, por lo que puede haber un costo (o agotamiento de la cuota [gratuita]) para tratar.

Divulgación completa: trabajo en Google en App Engine, y no tengo ninguna asociación con Cloudflare o Simon Willison.

1

Se lanzó recientemente el GAE firewall, destinado a reemplazar el anterior, bastante limitado, DoS Protection Service.

Es compatible con las actualizaciones programáticas de las reglas de firewall a través de la API de administración (REST): apps.firewall.ingressRules que podría combinarse con una pieza de lógica en la aplicación para la detección de DoS como se describe en otras respuestas. La diferencia sería que una vez que se implementa la regla, las solicitudes infractoras ya no incurrirán en cargos ya que no llegan a la aplicación, por lo que el filtrado en la aplicación en sí no es necesario.