Вопрос

Я рассматриваю возможность разработки приложения для Google App Engine, которое не должно получать слишком много трафика.Я бы действительно предпочел не платить за превышение бесплатных квот.Однако, похоже, было бы довольно легко вызвать атаку типа "отказ в обслуживании", перегрузив приложение и превысив квоты.Существуют ли какие-либо методы предотвращения или усложнения превышения бесплатных квот?Я знаю, что мог бы, например, ограничить количество запросов с IP-адреса (усложнив превышение квоты процессора), но есть ли какой-нибудь способ усложнить превышение запросов или квот пропускной способности?

Это было полезно?

Решение

Нет встроенных инструментов для предотвращения DoS.Если вы пишете приложения Google с использованием java, то вы можете использовать service.FloodFilter Фильтр.Следующий фрагмент кода будет выполнен раньше, чем любой из ваших сервлетов.

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

Если вы используете python, то вам, возможно, придется создать свой собственный фильтр.

Другие советы

Я не уверен, возможно ли это, но часто задаваемые вопросы по App Engine укажите, что если вы сможете доказать, что это атака DOS, то они возместят все сборы, связанные с этой атакой.

Похоже, что теперь у них есть фильтр на основе IP-адресов, доступный как для Python, так и для Java (я знаю, что это старая ветка, но она все еще высоко оценивается поиском в Google).

https://developers.google.com/appengine/docs/python/ конфигурации / DOS

Всегда возможно использовать службу, предоставляющую функции защиты от отказа в обслуживании, перед приложением App Engine. Например, Cloudflare предоставляет уважаемую службу https://www.cloudflare.com/waf/ , и есть другие. Насколько я понимаю (отказ от ответственности: я не использовал службу лично), что эти функции доступны в бесплатном плане.

Также довольно просто создать реализацию ограничения скорости на основе memcache в самом приложении. Вот первый хит, который я получил от поиска в Google по этому методу: http://blog.simonwillison.net / пост / 57956846132 / ratelimitcache . Этот механизм является надежным и может быть экономически эффективным, так как использование общей памяти может быть достаточным и бесплатным. Кроме того, если вы пойдете по этому маршруту, вы сможете контролировать ручки. Недостаток заключается в том, что само приложение должно обработать HTTP-запрос и принять решение о его разрешении или отклонении, поэтому может потребоваться дополнительная стоимость (или [бесплатное] исчерпание квоты).

Полное раскрытие. Я работаю в Google на App Engine и не имею отношения к Cloudflare или Саймону Уиллисону.

Тот Самый Брандмауэр GAE был недавно выпущен, призванный заменить предыдущий, довольно ограниченный, Служба защиты DoS.

Он поддерживает программные обновления правил брандмауэра через API администратора (REST): приложения.брандмауэр.Правила входа который мог бы быть объединен с встроенной логикой для обнаружения DoS, как описано в других ответах.Разница будет заключаться в том, что после развертывания правила за оскорбительные запросы больше не будут взиматься сборы, поскольку они больше не доходят до приложения, поэтому сама фильтрация в приложении не нужна.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top