Pregunta

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?

¿Fue útil?

Solución

No hay herramientas integradas para prevenir DoS. Si está escribiendo Google Apps usando java, puede usar el filtro service.FloodFilter . El siguiente fragmento 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á usando python, es posible que tenga que rodar su propio filtro.

Otros consejos

No estoy seguro de si es posible, pero Preguntas frecuentes de App Engine indica que si puedes mostrar que es un ataque de DOS, te reembolsarán los aranceles asociados con el ataque.

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

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

Siempre es posible usar 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 funciones 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 la 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 que lidiar.

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

El cortafuegos GAE fue lanzado recientemente, 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 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 ya no alcanzan la aplicación, por lo que no es necesario el filtrado dentro de la aplicación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top