Web server DoS protection

Hello,

Is there any protection from DoS attack at the web server level? I have a server with Tomcat and I found a way to make DoS attack from only client. Every site as a "heavy" page that occupy more time to process than others e.g. one second (a complex query, etc...). Statistically this page is requested very seldom and doesn't affect general server productivity. What can do an attacker?

1. First he find this "heavy" page.

2. Second, opens a 200 connections with this host (a default number of Tomcat worker threads).

3. Simultaneously sends GET http://host/heavy_page.jsp HTTP/1.1 to all of this connections.

A tomcat server is down for a long period of time (200 seconds) which may be used to recreate connections and repeat the operation. The server is at 100% busy.

What is the solution? Apart to use expensive hardware balancing solutions it may be resolved implementing a kind of filter for web server. The main idea is to compute simultaneous requests from one client (per ip) and send him a busy page if its' number is too much. Other solution is to increment response delay time with every new request went from one client in a small period of time.

I think the problem is clear and my question is: are there any standard solutions already written to solve it?

Un saludo,

Ant髇

[1355 byte] By [throwablea] at [2007-11-26 15:22:59]
# 1

Hello,

I didn't found any solution and I wrote my filter that can limit requests per client or per page. Here is a filter code.

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();

}

}

Other problem that appears is that one client may open a large number of idle connections with a web server without sending a request. That makes I/O operation very slow on the whole system. For example if I open 200 idle connections, a simple page load time becomes 1 minute, while Tomcat doesn't occupy processor time at all. I think there is no possibility to limit simultaneous connections from one host without changing tomcat source code.

Un saludo,

Ant髇

throwablea at 2007-7-8 21:38:12 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...