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.
-
Rate Limiting: The
requestCount
variable is used to count the number of requests received per second. If the count exceedsMAX_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 theinit
method. -
Input Validation: The
param
request parameter is validated using theStringUtils.isNumeric
method from Apache Commons Lang. If the parameter is not a valid number, the server responds with a 400 (Bad Request) error. -
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.