Я рассматриваю возможность разработки приложения для Google App Engine, которое не должно тратить слишком много трафика. Я бы действительно не заплатил, чтобы превысить бесплатные квоты. Однако, похоже, было бы довольно легко вызвать отказ в обслуживании путем перегрузки приложения и превышения квот. Существуют ли какие-либо способы предотвратить или затруднить превышение свободных квот? Я знаю, что я мог бы, например, ограничить количество запросов от IP (что затрудняет превышение квоты ЦП), но есть ли способ сделать его более сложным для превышения запросов или полос пропускания?
Можно ли предотвратить DoSing в Google App Engine?
Ответ 1
Нет встроенных инструментов для предотвращения DoS. Если вы пишете Google Apps с помощью 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, вам может понадобиться свернуть собственный фильтр.
Ответ 2
Я не уверен, возможно ли это, но Часто задаваемые вопросы в Engine Engine указывают, что если вы можете показать это DOS-атаку, тогда они возместить любые сборы, связанные с атакой.
Ответ 3
Кажется, у них теперь есть фильтр на основе IP-адресов, доступный как для Python, так и для Java (я знаю, что это старый поток, но он по-прежнему высок в поиске Google).
https://developers.google.com/appengine/docs/python/config/dos
Ответ 4
Всегда можно использовать службу, которая предоставляет функции защиты от отказа в обслуживании перед приложением App Engine. Например, Cloudflare обеспечивает уважаемый сервис https://www.cloudflare.com/waf/, и есть другие. Это мое понимание (отказ от ответственности: я лично не пользовался услугой), что эти функции доступны в свободном плане.
Также довольно легко построить реализацию ограничения скорости на основе memcache в вашем приложении. Вот первый хит, который я получил от поиска в Google для этого метода: http://blog.simonwillison.net/post/57956846132/ratelimitcache. Этот механизм звучит и может быть экономически эффективным, поскольку использование общей памяти может быть достаточным и бесплатным. Кроме того, переход по этому маршруту позволяет вам управлять ручками. Недостатком является то, что само приложение должно обрабатывать HTTP-запрос и принимать решение разрешить или отклонить его, поэтому может возникнуть проблема с затратами (или [свободным] квотами).
Полное раскрытие информации: я работаю в Google на App Engine и не имею никакого отношения к Cloudflare или Саймону Виллисону.
Ответ 5
Недавно был выпущен брандмауэр GAE, предназначенный для замены предыдущего, ограниченного Служба защиты DoS.
Он поддерживает программные обновления правил брандмауэра через API-интерфейс администратора (REST): apps.firewall.ingressRules, который может быть объединен с часть приложения логики для обнаружения DoS, как описано в других ответах. Разница заключалась бы в том, что после того, как правило будет развернуто, оскорбительные запросы больше не будут взимать плату, поскольку они больше не доходят до приложения, поэтому сама фильтрация в приложении не нужна.