type standard google features engine docs developers app google-app-engine resource-management

standard - ¿Es posible evitar DoSing en Google App Engine?



google app engine memory limit (5)

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


El firewall GAE fue lanzado recientemente, destinado a reemplazar el anterior, bastante limitado, Servicio de Protección DoS .

Es compatible con las actualizaciones programáticas de las reglas del cortafuegos a través de la API de administración (REST): apps.firewall.ingressRules que se pueden combinar con una lógica dentro de 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 ofensivas ya no incurrirán en cargos ya que no alcanzan la aplicación, por lo que no es necesario el filtrado dentro de la aplicación.


No estoy seguro de si es posible, pero las preguntas frecuentes de App Engine indican que si puedes mostrar que es un ataque de DOS, te reembolsarán las tarifas asociadas con el ataque.



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

También es bastante fácil construir una implementación de limitación de velocidad basada en memcache en su propia aplicación. Aquí está el primer hit 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 es gratuito. Además, recorrer esta ruta te pone en control de los mandos. El inconveniente es que la aplicación en sí misma debe manejar la solicitud HTTP y decidir permitirla o denegarla, por lo que puede haber un costo (o agotamiento de cuota [gratuito]) con el cual lidiar.

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


No hay herramientas integradas para prevenir DoS. Si está escribiendo Google Apps usando java, puede usar el service.FloodFilter . Filtro de filtro de service.FloodFilter . El siguiente fragmento de código se ejecutará antes de que lo haga 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á usando python, entonces puede que tenga que rodar su propio filtro.