I'm considering developing an app for Google App Engine, which should not get too much traffic. I'd really rather not pay to exceed the free quotas. However, it seems like it would be quite easy to cause a denial of service attack by overloading the app and exceeding the quotas. Are there any methods to prevent or make it harder to exceed the free quotas? I know I could, for example, limit the number of requests from an IP (making it harder to exceed the CPU quota), but is there any way to make it harder to exceed the requests or bandwidth quotas?
5 Answers
There are no built-in tools to prevent DoS. If you are writing Google Apps using java then you can use the service.FloodFilter
filter. The following piece of code will execute before any of your Servlets do.
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();
}
}
If you are using python, then you may have to roll your own filter.

- 446
- 3
- 8
-
I'm probably going to use Java for the extra speed, so this might help. – Zifre May 04 '09 at 22:02
-
App Engine has had DoS filter support for some time now. – Nick Johnson May 16 '12 at 05:13
-
3DOS filter only works for known IP right? It can't deal with DDOS attacks which IP are not known before attack starts. Also, the example above cannot protect static resources bandwidth usage – user7180 Apr 14 '14 at 15:39
I'm not sure if it's possible, but the App Engine FAQs indicate that if you can show it's a DOS attack then they'll refund any fees associated with the attack.

- 47,151
- 38
- 123
- 143
-
Thanks... if I were to pay for it, that would make me feel much better about this issue. – Zifre May 04 '09 at 22:04
-
4Unless you enable billing, exceeding the free quotas will simply take your site offline for a short period (much less than a whole day). It'll only bill you if you've explicitly enabled that, and you can set your own billing cap. – Nick Johnson May 05 '09 at 07:36
-
3I had experienced DOS attack on a static file download (something like 20MB x 600 times in 2 hours) from a single IP, I asked for refund and they refused, saying this is not considered as DOS attack. And they say if the service stopped because of reaching the daily budget you set, this is not considered as "denial". I'd say we better invent our own way to protect from DOS attack for now until Google fixed their problem. – user7180 Nov 11 '13 at 09:57
It seems that they have an IP-address based filter available for both Python and Java now (I know this is an old thread, but it still comes up high on a Google search).
https://developers.google.com/appengine/docs/python/config/dos

- 18,332
- 10
- 49
- 53
-
3This is not useful in any DDoS attack, number of users is much greater than the 1000 IP's that you can block with that tool. Not to mention that you have to re-upload your website every few minutes as the new attackers get involved in the attack. – Igor Jerosimić Nov 18 '12 at 09:22
It's always possible to use a service that provides Denial of Service protection features in front of an App Engine application. For example, Cloudflare provides a well respected service https://www.cloudflare.com/waf/, and there are others. It's my understanding (disclaimer: I haven't used the service personally) that these features are available on the free plan.
It's also fairly easy to construct a memcache based rate limiting implementation in your application itself. Here's the first hit I got from a google search for this method: http://blog.simonwillison.net/post/57956846132/ratelimitcache. This mechanism is sound, and can be cost effective as shared memcache usage may suffice and is free. Furthermore, going this route puts you in control of the knobs. The drawback is that the application itself must handle the HTTP request and decide to allow or deny it, so there may be cost (or [free] quota exhaustion) to deal with.
Full Disclosure: I work at Google on App Engine, and have no association with Cloudflare or Simon Willison.

- 11
- 2
The GAE firewall was recently released, intended to replace the previous, rather limited, DoS Protection Service.
It supports programmatic updates of the firewall rules via the (REST) Admin API: apps.firewall.ingressRules which could be combined with an in-app piece of logic for DoS detection as described in other answers. The difference would be that once the rule is deployed the offending requests will no longer incur charges as they don't reach the app anymore, so the in-app filtering itself is not needed.

- 39,470
- 12
- 57
- 97
-
how can we call this API REST from our server if they are asking for Oauth2 to use it? https://cloud.google.com/appengine/docs/admin-api/reference/rest/v1beta/apps.firewall.ingressRules/create?apix=true – Ulises CT Dec 30 '19 at 00:22