Skip to main content

Asymmetric denial of service

Need

The need is to implement measures that prevent or mitigate asymmetric denial of service attacks, ensuring the server remains responsive and available to legitimate users.

Context

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

Description

Non compliant code

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class VulnerableServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String param = request.getParameter("param");
if (param != null) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
response.getWriter().write("Response " + i + "\\n");
}
}
}
}

The above servlet VulnerableServlet is vulnerable to an asymmetric denial of service attack. It's a simple HTTP servlet that listens for GET requests. When it receives a request, it checks if the request contains a parameter named "param". If it does, it enters an infinite loop that writes a large number of responses back to the client.

The vulnerability lies in the loop. By setting the loop to iterate Integer.MAX_VALUE times, it's possible to send a single request that will cause the server to generate an enormous number of responses. This is an example of an amplification attack, where a single request can consume a large amount of server resources.

Additionally, if a malicious request is sent with a very large "param" value, it can cause the server to consume all of its available memory, causing it to become unresponsive. This is a type of denial of service attack.

This code is vulnerable because it does not limit the number of responses it can generate from a single request, and it does not validate or limit the size of the "param" value. This allows an attacker to send a single request that can consume all of the server's resources, causing a denial of service.

Steps

  • Implement rate limiting to prevent amplification attacks
  • Validate and sanitize user input to prevent malicious requests
  • Implement resource limits to prevent excessive resource consumption

Compliant code

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;

public class SecureServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final int MAX_REQUESTS_PER_SECOND = 5;
private AtomicInteger requestCount = new AtomicInteger(0);

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (requestCount.incrementAndGet() > MAX_REQUESTS_PER_SECOND) {
response.sendError(HttpServletResponse.SC_TOO_MANY_REQUESTS, "Too many requests");
return;
}

String param = request.getParameter("param");
if (StringUtils.isNumeric(param)) {
int limit = Integer.parseInt(param);
if (limit > 1000) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request limit exceeded");
return;
}

for (int i = 0; i < limit; i++) {
response.getWriter().write("Response " + i + "\\n");
}
} else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid request");
}

requestCount.decrementAndGet();
}

@Override
public void init() throws ServletException {
super.init();
new Thread(() -> {
while (true) {
requestCount.set(0);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}

The above code is a secure version of the original servlet. It implements rate limiting, input validation, and resource limits to prevent asymmetric denial of service attacks.

  1. Rate Limiting: The requestCount variable is used to count the number of requests received per second. If the count exceeds MAX_REQUESTS_PER_SECOND, the server responds with a 429 (Too Many Requests) error. The count is reset every second by a background thread started in the init method.

  2. Input Validation: The param request parameter is validated using the StringUtils.isNumeric method from Apache Commons Lang. If the parameter is not a valid number, the server responds with a 400 (Bad Request) error.

  3. Resource Limits: The param request parameter is also used to limit the number of responses generated by the server. If the parameter value exceeds 1000, the server responds with a 400 (Bad Request) error. This prevents a single request from consuming an excessive amount of server resources.

By implementing these measures, the server can effectively mitigate the risk of asymmetric denial of service attacks.

References