Prevention of service outages caused by repeated resource-intensive requests
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class VulnerableServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
while(true) {
// A resource-intensive operation
calculateFibonacci(1000000);
}
}
private int calculateFibonacci(int n) {
if (n <= 1) return n;
else return calculateFibonacci(n-1) + calculateFibonacci(n-2);
}
}
The above code represents a servlet in Java which is vulnerable to a symmetric denial of service attack. The servlet is defined as
VulnerableServlet
and it extends
HttpServlet
class provided by
javax.servlet.http
package.
In the servlet, there's a
doGet
method which is called on every GET request. Inside this method, there's an infinite loop where a resource-intensive operation is performed. This operation is the calculation of a large Fibonacci number (1,000,000th Fibonacci number) which is a very expensive operation in terms of CPU and memory usage.
The
calculateFibonacci
method is a recursive function which calculates the Fibonacci number of a given integer. This function has an exponential time complexity due to the repeated calculations of the same Fibonacci numbers.
This code is vulnerable because an attacker can repeatedly send GET requests to the server, causing the server to continuously calculate large Fibonacci numbers. Since this operation is very resource-intensive, the server will eventually run out of resources and become unresponsive, resulting in a denial of service.
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.*;
public class FixedServlet extends HttpServlet {
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
private final Semaphore semaphore = new Semaphore(5); // Limit to 5 requests at a time
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
if (!semaphore.tryAcquire()) {
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return;
}
Future future = executorService.submit(() -> calculateFibonacci(1000000));
try {
future.get(5, TimeUnit.SECONDS); // Timeout after 5 seconds
} catch (InterruptedException | ExecutionException | TimeoutException e) {
future.cancel(true);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
semaphore.release();
}
}
private int calculateFibonacci(int n) {
if (n <= 1) return n;
else return calculateFibonacci(n-1) + calculateFibonacci(n-2);
}
}
The fixed code implements several strategies to mitigate the symmetric denial of service vulnerability:
1. Rate Limiting: A
Semaphore
is used to limit the number of concurrent requests that can be processed by the servlet. If the semaphore cannot acquire a permit, it means the rate limit has been reached and the server responds with a
503 Service Unavailable
status.
2. Asynchronous Processing: The resource-intensive operation is submitted to an
ExecutorService
for asynchronous processing. This allows the server to continue handling other requests without being blocked by the long-running operation.
3. Timeouts: The
Future.get
method is used with a timeout of 5 seconds. If the operation does not complete within this time, a
TimeoutException
is thrown and the operation is cancelled. The server responds with a
500 Internal Server Error
status.
4. Error Handling: Proper error handling is implemented to gracefully handle exceptions and respond with appropriate HTTP status codes.
Note: The
calculateFibonacci
method is still very inefficient and could be optimized, for example, by using dynamic programming or matrix exponentiation. However, this is beyond the scope of this fix.