谷歌应用引擎上是否有防止DoS攻击的可能性?

18

我正在考虑开发一个Google App Engine应用程序,应该不会吸引太多的流量。我真的不想支付超过免费配额的费用。然而,看起来很容易通过超载应用程序和超过配额来造成拒绝服务攻击。有没有方法可以防止或使超出免费配额更加困难?例如,我知道可以限制来自IP的请求数量(使超过CPU配额更加困难),但是否有任何方法可以使超过请求或带宽配额更加困难?

5个回答

16

没有内置工具来防止DoS攻击。如果你在使用Java编写Google应用程序,那么可以使用service.FloodFilter过滤器。以下代码将在任何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();
    }
}

如果你正在使用 Python,那么你可能需要自己编写过滤器。


我可能会使用Java来获得额外的速度,所以这可能会有所帮助。 - Zifre
App Engine现在已经有了DoS过滤器支持。 - Nick Johnson
3
DOS过滤器只能处理已知IP的攻击,无法应对IP在攻击开始前就未知的DDOS攻击。此外,上述示例无法保护静态资源的带宽使用。 - user7180

7

我不确定是否可能,但是App Engine FAQs表明,如果您能够证明这是DOS攻击,则他们将退还与攻击相关的任何费用。


谢谢...如果我付钱解决这个问题,那会让我感觉好多了。 - Zifre
4
如果您不开启计费,超出免费配额将会导致您的网站暂时下线(时间远远少于一整天)。只有在明确开启了计费,并且您可以设置自己的计费上限,才会收取费用。请注意,此处不会对计费进行解释。 - Nick Johnson
3
我曾经遭受过来自单个IP地址的DOS攻击,攻击针对的是一个静态文件下载链接(大约20MB重复下载了600次,在2小时内完成)。我要求退款,但服务提供商拒绝,���称这不算是DOS攻击。他们还说,如果服务因达到您设置的每日预算而停止,那也不算“阻止访问”。我认为我们最好自己想办法保护自己免受DOS攻击,直到谷歌解决他们的问题。 - user7180

2

3
在任何DDoS攻击中这都没有用处,因为用户数量远大于你可以使用此工具阻止的1000个IP地址。更不用提你还需要每隔几分钟重新上传你的网站,以应对新加入攻击的黑客。 - Igor Jerosimić

1
在App Engine应用程序前使用提供拒绝服务保护功能的服务始终是可能的。例如,Cloudflare提供了一个备受尊重的服务https://www.cloudflare.com/waf/,还有其他类似的服务。据我所知(免责声明:我个人没有使用过该服务),这些功能在免费计划中也可用。
此外,在应用程序本身中构建基于memcache的速率限制实现也相当容易。这是我从谷歌搜索中得到的第一个结果http://blog.simonwillison.net/post/57956846132/ratelimitcache。这种机制很可靠,而且可以成本效益高,因为共享的memcache使用可能足够并且是免费的。此外,采用这种方法可以让您掌握控制权。缺点是应用程序本身必须处理HTTP请求并决定是否允许或拒绝它,因此可能需要付费(或[免费]配额用尽)来处理。
全面披露:我在Google的App Engine上工作,与Cloudflare或Simon Willison没有任何关联。

1

最近发布了GAE防火墙,旨在取代之前相对有限的DoS保护服务

它支持通过(REST)管理API:apps.firewall.ingressRules编程更新防火墙规则,可以与其他答案中描述的用于DoS检测的应用程序逻辑结合使用。不同之处在于,一旦部署了规则,有问题的请求将不再产生费用,因为它们不再到达应用程序,因此不需要进行应用内过滤。


如果API要求使用Oauth2,我们怎么从服务器调用REST API呢? https://cloud.google.com/appengine/docs/admin-api/reference/rest/v1beta/apps.firewall.ingressRules/create?apix=true - Ulises CT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接