logo

Symmetric denial of service - Java


Need

Prevention of service outages caused by repeated resource-intensive requests


Context

  1. Usage of Java 8 for developing applications with enhanced features and performance
  2. Usage of javax.servlet-api for building Java web applications with servlets

Description

Insecure Code Example

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.

Steps

  1. Implement rate limiting to restrict the number of requests a client can make within a certain time period.
  2. Optimize the resource-intensive operation to reduce the amount of resources it consumes.
  3. Implement timeouts for requests to prevent them from taking too long to be processed.
  4. Consider using asynchronous processing or background tasks for long-running operations to avoid blocking the server.
  5. Monitor server performance and resource usage to identify and address any bottlenecks or inefficiencies.
  6. Implement proper error handling and response codes to gracefully handle excessive requests.

Secure Code Example

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<Integer> 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.


References

  • 003 - Symmetric denial of service

  • Last updated

    2023/09/18