Domanda

Sto pensando di sviluppare un'app per Google App Engine, che non dovrebbe ricevere troppo traffico. Preferirei davvero 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 impedire 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 il superamento della quota della CPU), ma esiste un modo per rendere più difficile il superamento delle richieste o delle quote di larghezza di banda?

È stato utile?

Soluzione

Non ci sono strumenti integrati per impedire DoS. Se stai scrivendo Google Apps utilizzando java, puoi utilizzare il filtro service.FloodFilter . Il seguente codice verrà eseguito prima di qualsiasi 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 stai usando Python, potresti dover rotolare il tuo filtro.

Altri suggerimenti

Non sono sicuro che sia possibile, ma le Domande frequenti su App Engine indica che se puoi dimostrare che si tratta di un attacco DOS, ti rimborseranno tutte le commissioni associate all'attacco.

Sembra che abbiano un filtro basato su indirizzo IP disponibile sia per Python che per Java (so che si tratta di un vecchio thread, ma risulta ancora in cima a una ricerca su Google).

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

È sempre possibile utilizzare un servizio che fornisce funzionalità di protezione Denial of Service di fronte a un'applicazione App Engine. Ad esempio, Cloudflare offre un servizio rispettato https://www.cloudflare.com/waf/ , e ce ne sono altri. Sono consapevole (disclaimer: non ho usato il servizio personalmente) che queste funzionalità sono disponibili nel piano gratuito.

È anche abbastanza facile costruire un'implementazione che limita la velocità basata su memcache nella stessa applicazione. Ecco il primo successo 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 in quanto l'utilizzo di memcache condiviso può essere sufficiente ed è gratuito. Inoltre, seguendo questo percorso, avrai il controllo delle manopole. Lo svantaggio è che l'applicazione stessa deve gestire la richiesta HTTP e decidere di consentirla o negarla, quindi potrebbero esserci costi (o esaurimento delle quote [gratuiti]) da gestire.

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

Il firewall GAE è stato recentemente rilasciato, 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 potrebbe essere combinato con un pezzo di logica in-app per il rilevamento di DoS come descritto in altre risposte. La differenza sarebbe che una volta implementata la regola le richieste offensive non comportano più addebiti in quanto non raggiungono più l'app, quindi il filtro in-app stesso non è necessario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top