Overview
CVE-2023-20883 describes an unrestricted resource consumption vulnerability in Spring Boot apps that rely on Spring MVC when used behind a reverse proxy cache. In affected versions (3.0.0-3.0.6, 2.7.0-2.7.11, 2.6.0-2.6.14, 2.5.0-2.5.14 and older unsupported releases), crafted requests can cause DoS by exhausting CPU and memory resources due to how request parameters and caching interplay with the proxy. This can lead to thread pool saturation and degraded service for legitimate users when an attacker floods the backend through the proxy cache chain. The issue maps to CWE-400 (Uncontrolled Resource Consumption) and is triggered by scenarios where Spring MVC processes requests with many parameters or heavy payloads in a way that is not bounded by server or application logic when proxied behind a cache. This vulnerability is a classic DoS vector that attackers can exploit without needing to breach authentication or authorization on the application itself. The remediation is to upgrade to a patched Spring Boot version and to implement defense-in-depth with input size/parameter limiting and proactive request-rate controls. This CVE is specifically called out for Spring Boot environments that incorporate Spring MVC with reverse proxy caching layers.
Affected Versions
3.0.0-3.0.6, 2.7.0-2.7.11, 2.6.0-2.6.14, 2.5.0-2.5.14 and older unsupported versions
Code Fix Example
Spring Boot API Security Remediation
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class App {
public static void main(String[] args) { SpringApplication.run(App.class, args); }
// path constants to avoid inline string literals in annotations
private static final String VULN_PATH = "/vulnerable/process";
private static final String FIX_PATH = "/fixed/process";
@RestController
public static class VulnerableController {
@GetMapping(VULN_PATH)
public String vulnerableProcess(@RequestParam Map<String, String> params) {
StringBuilder sb = new StringBuilder();
// Vulnerable pattern: no bounding on CPU/memory work per request
for (String v : params.values()) {
sb.append(v);
// simulate expensive work per parameter (unbounded in practice)
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
}
return "Processed " + params.size() + " params, chars=" + sb.length();
}
}
@RestController
public static class FixedController {
private static final int MAX_PARAM_COUNT = 1000;
private static final int MAX_TOTAL_PARAM_LENGTH = 10000; // total chars across all params
@GetMapping(FIX_PATH)
public String fixedProcess(@RequestParam Map<String, String> params) {
if (params.size() > MAX_PARAM_COUNT) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
long totalLen = 0;
for (String s : params.values()) totalLen += s.length();
if (totalLen > MAX_TOTAL_PARAM_LENGTH) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
StringBuilder sb = new StringBuilder();
for (String v : params.values()) sb.append(v);
return "Processed " + params.size() + " params, chars=" + sb.length();
}
}
@Component
public static class LimitRequestSizeFilter implements Filter {
private static final long MAX_REQUEST_SIZE = 2 * 1024 * 1024; // 2 MB
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
long contentLength = httpReq.getContentLengthLong();
if (contentLength > MAX_REQUEST_SIZE) {
HttpServletResponse httpResp = (HttpServletResponse) response;
httpResp.setStatus(HttpStatus.REQUEST_ENTITY_TOO_LARGE.value());
return;
}
chain.doFilter(request, response);
}
}
}