Czy można zapobiec dozowaniu na Google App Engine?

Rozważam stworzenie aplikacji dla Google App Engine, która nie powinna generować zbyt dużego ruchu. Wolałbym nie płacić za przekroczenie wolnych limitów. Wydaje się jednak, że dość łatwo byłoby wywołać atak typu denial of service, przeciążając aplikację i przekraczając limity. Czy są jakieś metody, aby zapobiec lub utrudnić przekroczenie wolnych kwot? Wiem, że mógłbym np. ograniczyć liczbę żądań z IP (co utrudnia przekroczenie limitu CPU), ale czy są jakieś sposób, aby utrudnić przekraczanie żądań lub limitów przepustowości?

Author: Zifre, 2009-05-05

5 answers

Nie ma wbudowanych narzędzi zapobiegających DoS. Jeśli piszesz Google Apps używając Javy, możesz użyć filtra service.FloodFilter. Poniższy fragment kodu zostanie wykonany przed wykonaniem któregokolwiek z Twoich serwletów.

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

Jeśli używasz Pythona, być może będziesz musiał użyć własnego filtra.

 16
Author: Achille,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-04 21:55:35

Nie jestem pewien, czy to możliwe, ale App Engine FAQs wskazują, że jeśli pokażesz, że to atak DOS, zwrócą wszelkie opłaty związane z atakiem.

 7
Author: Jeremy Logan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-04 21:54:26

Wygląda na to, że mają filtr oparty na adresie IP dostępny teraz zarówno dla Pythona, jak i Javy (wiem, że to stary wątek, ale wciąż pojawia się wysoko w wyszukiwarce Google).

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

 2
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-15 02:27:22

Zawsze można korzystać z usługi, która zapewnia funkcje ochrony przed odmową usługi przed aplikacją App Engine. Na przykład Cloudflare zapewnia szanowaną usługę https://www.cloudflare.com/waf / , i są inne. Rozumiem (zastrzeżenie: nie korzystałem osobiście z usługi), że te funkcje są dostępne w planie darmowym.

Dość łatwo jest również skonstruować implementację ograniczającą szybkość memcache w Twojej aplikacji siebie. Oto pierwszy hit jaki dostałem z wyszukiwarki google dla tej metody: http://blog.simonwillison.net/post/57956846132/ratelimitcache . ten mechanizm jest solidny i może być opłacalny, ponieważ współdzielone użycie memcache może wystarczyć i jest bezpłatne. Co więcej, Przejście tej trasy daje Ci kontrolę nad pokrętłami. Wadą jest to, że sama aplikacja musi obsłużyć żądanie HTTP i zdecydować się na pozwolenie lub odrzucenie go, więc może być koszt (lub [bezpłatne] wyczerpanie kwoty) do obsługi z.

Pełne ujawnienie: pracuję w Google na silniku aplikacji i nie mam związku z Cloudflare lub Simon Willison.

 1
Author: Tad Hunt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 18:21:41

Gae firewall został niedawno wydany, mający zastąpić poprzednią, dość ograniczoną, usługę ochrony DoS.

Obsługuje programową aktualizację reguł zapory poprzez API (REST) Admin: apps.firewall.ingressRules, które można połączyć z logiką w aplikacji do wykrywania DoS, jak opisano w innych odpowiedziach. Różnica polega na tym, że po wdrożeniu reguły żądania obrażeń nie będą już ponosić opłat, ponieważ nie dotrzyj do aplikacji już, więc samo filtrowanie w aplikacji nie jest potrzebne.

 1
Author: Dan Cornilescu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-26 03:49:18