2009-05-04 15 views
16

Google App Engine用のアプリを開発することを検討していますが、あまりトラフィックを増やすべきではありません。私は本当にむしろ無料のクォータを超えるために支払うことはありません。しかし、アプリケーションのオーバーロードとクォータの超過によってサービス拒否攻撃を引き起こすことは非常に簡単なようです。フリークォータを超えることを防ぐか、それを難し​​くする方法はありますか?たとえば、IPからの要求数を制限することができます(CPUクォータを超過することは難しくなります)が、要求や帯域幅クォータを超えることを困難にする方法はありますか?Google App EngineでDoSingを防止できますか?

答えて

16

DoSを防止するための組み込みツールはありません。 javaを使用してGoogle Appsを作成する場合は、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を使用している場合は、独自のフィルタをロールバックする必要があります。

+0

私は多分余分な速度のためにJavaを使用するつもりです、これは助けるかもしれません。 – Zifre

+0

App Engineは、しばらくの間、DoSフィルタをサポートしていました。 –

+3

DOSフィルタは既知のIPに対してのみ動作しますか?攻撃が始まる前にIPが知られていないDDOS攻撃に対応することはできません。また、上記の例では静的リソースの帯域幅使用量を保護できません –

7

可能かどうかわかりませんが、App Engine FAQsは、それがDOS攻撃であることを示すことができれば、攻撃に関連するすべての料金を払い戻すことを示しています。

+0

ありがとう...もし私がそれを支払うなら、それは私にこの問題についてもっと良く感じさせるでしょう。 – Zifre

+3

請求を有効にしない限り、無料のクォータを超えると、短期間(一日をはるかに下回る)サイトがオフラインになります。明示的に有効にした場合にのみ請求され、独自の請求上限を設定することができます。 –

+3

私は静的なファイルダウンロード(2時間で20MB x 600回のようなもの)でDOS攻撃を経験しましたが、払い戻しを求めて拒否しました。これはDOS攻撃とはみなされないと言っています。あなたが設定した1日の予算に達したためにサービスが停止した場合、これは「拒否」とはみなされません。 私は、Googleが問題を解決するまで、今のところDOS攻撃から保護する独自の方法を発明するほうがよいと言いたいと思います。 –

2

これはPythonとJavaの両方で使用可能なIPアドレスベースのフィルタを持っているようです(これは古いスレッドだとわかっていますが、まだGoogle検索では高くなっています)。

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

+3

これはDDoS攻撃には有効ではありません。ユーザー数は、そのツールでブロックできる1000個のIP数をはるかに上回ります。新しい攻撃者が攻撃に巻き込まれるにつれて、あなたのWebサイトを数分おきに再アップロードする必要はありません。 –

1

これは、App Engineアプリケーションの前にサービス保護機能の否定を提供するサービスを利用することは常に可能です。たとえば、Cloudflareはよく尊敬されるサービスであるhttps://www.cloudflare.com/waf/を提供しています。これらの機能は無料のプランで利用できるということは私の理解です(免責事項:私はサービスを個人的に使用していません)。

アプリケーション自体にmemcacheベースのレート制限を実装することもかなり簡単です。ここで私はこの方法のためのGoogleの検索から得た最初のヒットです:http://blog.simonwillison.net/post/57956846132/ratelimitcache。このメカニズムは妥当であり、共有memcacheの使用で十分であり、無料であるため、コスト効率がよい場合があります。さらに、このルートを使用すると、ノブのコントロールが可能になります。欠点は、アプリケーション自体がHTTPリクエストを処理し、それを許可または拒否する必要があるため、処理するコスト(または[空き容量]の枯渇)がある可能性があることです。

フル開示:私はApp Engine上でGoogleで働き、CloudflareやSimon Willisonとは関連していません。

1

GAE firewallは、以前はかなり限定されたDoS Protection Serviceを置き換えることを意図して、最近リリースされました。

これは、(REST)Admin API:apps.firewall.ingressRulesを介して、ファイアウォールルールのプログラムによる更新をサポートしています。これは、他の回答に記載されているようにDoS検出用のアプリ内ロジックと組み合わせることができます。違いは、ルールが展開されると、違反リクエストはもはやアプリに到達しないため、料金が発生しなくなるため、アプリ内フィルタリング自体は必要ないということです。