Question

J'envisage de développer une application pour Google App Engine, qui ne devrait pas générer trop de trafic. Je préférerais vraiment ne pas payer pour dépasser les quotas gratuits. Cependant, il semble assez facile de provoquer une attaque par déni de service en surchargeant l'application et en dépassant les quotas. Existe-t-il des méthodes pour empêcher ou rendre plus difficile le dépassement des quotas gratuits? Je sais que je pourrais, par exemple, limiter le nombre de requêtes provenant d’une adresse IP (ce qui rend plus difficile le dépassement du quota de processeur), mais existe-t-il un moyen de rendre plus difficile le dépassement des requêtes ou des quotas de bande passante?

Était-ce utile?

La solution

Il n’existe aucun outil intégré permettant d’empêcher les dénis de service. Si vous écrivez Google Apps avec Java, vous pouvez utiliser le filtre service.FloodFilter . Le code suivant s’exécutera avant l’un de vos 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 vous utilisez python, vous devrez peut-être lancer votre propre filtre.

Autres conseils

Je ne sais pas si c'est possible, mais la FAQ sur le moteur d'applications indique que si vous pouvez montrer que c'est une attaque DOS, ils vous rembourseront les frais associés à l'attaque.

Il semble qu’ils disposent maintenant d’un filtre basé sur l’adresse IP pour Python et Java (je sais que c’est un ancien fil de discussion, mais il reste élevé dans les recherches Google).

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

Il est toujours possible d'utiliser un service fournissant des fonctionnalités de protection contre le déni de service devant une application App Engine. Par exemple, Cloudflare fournit un service respecté https://www.cloudflare.com/waf/ , et il y en a d'autres. Si j'ai bien compris (je n'ai pas utilisé le service personnellement), ces fonctionnalités sont disponibles dans le forfait gratuit.

Il est également assez facile de construire une implémentation à limitation de débit basée sur memcache dans votre application elle-même. Voici le premier résultat d'une recherche Google pour cette méthode: http://blog.simonwillison.net / post / 57956846132 / ratelimitcache . Ce mécanisme est valable et peut être rentable, car l’utilisation de memcache partagée peut suffire et est gratuite. De plus, cette route vous permet de contrôler les boutons. L’inconvénient est que l’application elle-même doit gérer la requête HTTP et décider de l’autoriser ou de la refuser. Il peut donc y avoir un coût (ou l’épuisement du quota [libre]).

Divulgation complète: je travaille chez Google sur App Engine et je n’ai aucune association avec Cloudflare ou Simon Willison.

Le pare-feu GAE a récemment été publié, destiné à remplacer le précédent service de protection des dénis de service , plutôt limité .

Il prend en charge les mises à jour programmées des règles de pare-feu via l'API d'administration (REST): apps.firewall.ingressRules qui pourrait être combiné à un élément logique de l'application pour la détection de déni de service, comme décrit dans d'autres réponses. La différence serait que, une fois la règle déployée, les requêtes incriminées ne feront plus l'objet d'accusations car elles ne parviendront plus à l'application. Le filtrage intégré à l'application n'est donc pas nécessaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top