【问题标题】:Is it possible to prevent DoSing on Google App Engine?是否可以防止 Google App Engine 上的 DoSing?
【发布时间】:2010-10-23 18:17:48
【问题描述】:

我正在考虑为 Google App Engine 开发一个应用程序,它应该不会获得太多流量。我真的宁愿不支付超过免费配额的费用。但是,通过超载应用程序和超出配额似乎很容易引起拒绝服务攻击。是否有任何方法可以防止或使其更难超过免费配额?例如,我知道我可以限制来自 IP 的请求数量(使其更难超过 CPU 配额),但有什么方法可以让超过请求或带宽配额更难?

【问题讨论】:

    标签: google-app-engine resource-management


    【解决方案1】:

    没有防止 DoS 的内置工具。如果您使用 java 编写 Google Apps,那么您可以使用 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 来获得额外的速度,所以这可能会有所帮助。
    • App Engine 支持 DoS 过滤器已有一段时间了。
    • DOS 过滤器仅适用于已知 IP 对吗?它不能处理攻击开始前IP未知的DDOS攻击。另外,上面的例子不能保护静态资源带宽使用
    【解决方案2】:

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

    【讨论】:

    • 谢谢...如果我付钱,那会让我对这个问题感觉好多了。
    • 除非您启用计费,否则超出免费配额只会让您的网站在短时间内离线(远少于一整天)。仅当您明确启用它时才会向您收费,并且您可以设置自己的帐单上限。
    • 我遇到过单个IP对静态文件下载的DOS攻击(大概2小时20MB x 600次),我要求退款,他们拒绝了,说这不是DOS攻击.他们说如果服务因达到您设置的每日预算而停止,这不被视为“拒绝”。我想说,在 Google 解决他们的问题之前,我们最好先发明自己的方法来保护免受 DOS 攻击。
    【解决方案3】:

    似乎他们现在有一个可用于 Python 和 Java 的基于 IP 地址的过滤器(我知道这是一个旧线程,但它在 Google 搜索中仍然很高)。

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

    【讨论】:

    • 这在任何 DDoS 攻击中都没有用,用户数量远远大于您可以使用该工具阻止的 1000 个 IP。更不用说随着新的攻击者参与攻击,您必须每隔几分钟重新上传您的网站。
    【解决方案4】:

    始终可以在 App Engine 应用程序前使用提供拒绝服务保护功能的服务。例如,Cloudflare 提供了备受推崇的服务https://www.cloudflare.com/waf/,还有其他服务。我的理解(免责声明:我没有亲自使用过该服务)这些功能在免费计划中可用。

    在您的应用程序本身中构建基于内存缓存的速率限制实现也相当容易。这是我从谷歌搜索此方法获得的第一个结果:http://blog.simonwillison.net/post/57956846132/ratelimitcache。这种机制是合理的,并且具有成本效益,因为共享的 memcache 使用可能就足够了并且是免费的。此外,走这条路线可以让您控制旋钮。缺点是应用程序本身必须处理 HTTP 请求并决定允许或拒绝它,因此可能需要处理成本(或 [免费] 配额耗尽)。

    完全披露:我在 Google 的 App Engine 工作,与 Cloudflare 或 Simon Willison 没有任何关系。

    【讨论】:

      【解决方案5】:

      GAE firewall 最近发布,旨在取代之前相当有限的DoS Protection Service

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

      【讨论】:

      猜你喜欢
      • 2012-05-08
      • 2010-12-26
      • 2014-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-07
      相关资源
      最近更新 更多