2009-05-04 90 views
16

我正在考慮爲Google App Engine開發應用程序,該應用程序不應該獲得太多流量。我真的不想支付超過免費配額。但是,通過重載應用程序並超過配額,似乎很容易導致拒絕服務攻擊。是否有任何方法可以防止或使其難以超過免費配額?例如,我知道我可以限制來自IP的請求數量(使其難以超過CPU配額),但是有什麼方法可以使難以超出請求或帶寬配額嗎?是否可以防止Google App Engine上的DoSing?

回答

16

沒有防止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,那麼你可能推出自己的過濾器。

7

我不確定是否有可能,但App Engine FAQs表示如果您能顯示它是DOS攻擊,那麼他們將退還與此次攻擊相關的任何費用。

+0

謝謝......如果我付錢,那會讓我對這個問題感覺好多了。 – Zifre 2009-05-04 22:04:20

+3

除非您啓用結算功能,否則超出免費配額只會讓您的網站在短時間內離線(遠遠少於一整天)。如果您已明確啓用該功能,它只會向您收費,您可以設置自己的結算上限。 – 2009-05-05 07:36:26

+3

我曾經從一個IP地址下載靜態文件下載(類似於20MB×2小時的東西)的DOS攻擊,我要求退款,他們拒絕,說這不被視爲DOS攻擊。他們說,如果服務因達到您設定的每日預算而停止,這不會被視爲「拒絕」。 我想說我們現在可以更好地發明我們自己的方式來保護DOS攻擊,直到Google解決他們的問題。 – 2013-11-11 09:57:31

1

它總是可以使用,在App Engine應用程序的前面提供的服務保護功能,拒絕服務。例如,Cloudflare提供備受推崇的服務https://www.cloudflare.com/waf/,還有其他的。這是我的理解(免責聲明:我沒有親自使用過這些服務)這些功能在免費計劃中提供。

在應用程序本身中構建基於memcache的速率限制實現也相當容易。這是我從谷歌搜索這種方法獲得的第一個熱門影片:http://blog.simonwillison.net/post/57956846132/ratelimitcache。這種機制是健全的,並且可以具有成本效益,因爲共享內存緩存使用可能已足夠並且是免費的。此外,走這條路可以讓你控制旋鈕。缺點是應用程序本身必須處理HTTP請求並決定允許或拒絕它,所以可能會有成本(或[免費]配額耗盡)來處理。

完全披露:我在Google上的App Engine上工作,與Cloudflare或Simon Willison沒有任何關係。

1

最近發佈了GAE firewall,意在取代之前的,相當有限的DoS Protection Service

它支持通過(REST)管理API編程更新防火牆規則:apps.firewall.ingressRules可以與其他答案中描述的應用內DoS檢測邏輯相結合。不同之處在於,一旦部署規則,違規請求將不再產生費用,因爲它們不會再到達應用程序,因此不需要應用程序內過濾。

相關問題