OWASP API Security Top 4: Unrestricted Resource Consumption and How to Secure Your Spring Boot 3.x APIs

Master Spring Ter
6 min readNov 10, 2024

In the modern digital landscape, APIs are the lifeblood of applications, enabling seamless communication between services. However, this connectivity also opens doors to potential security vulnerabilities. One critical issue highlighted in the OWASP API Security Top 10 for 2023 is Unrestricted Resource Consumption. This article explores what this vulnerability entails, its impact, and how you can protect your Spring Boot 3.x APIs against it.

What Is Unrestricted Resource Consumption?

Unrestricted Resource Consumption refers to an API’s failure to limit the resources a client can consume. This can lead to Denial of Service (DoS) attacks, where the API becomes unavailable to legitimate users due to resource exhaustion. Resources affected can include CPU, memory, disk space, network bandwidth, or even application-specific resources like database connections.

Common Examples:

  • No Rate Limiting: Clients can make unlimited API requests, overwhelming the server.
  • Unbounded Data Fetching: APIs return large data sets without pagination or size limits.
  • Uncontrolled File Uploads: Users can upload excessively large files, consuming disk space.
  • Complex Computations: APIs perform intensive computations without execution time limits.

Potential Impact:

  • Service Downtime: Legitimate users cannot access the service.
  • Increased Costs: Higher cloud resource consumption leads to unexpected expenses.
  • Security Breaches: Resource exhaustion can be a precursor to other attacks.

Understanding Resource Consumption in Spring Boot 3.x

Spring Boot 3.x provides a robust framework for building APIs but does not enforce resource consumption limits out of the box. As a developer, you must implement controls to prevent abuse. Key areas to focus on include:

  • Rate Limiting: Controlling the number of requests a client can make in a given time frame.
  • Payload Size Limitation: Restricting the size of requests and responses.
  • Pagination and Data Limits: Limiting the amount of data returned in a single response.
  • Timeouts and Execution Limits: Setting limits on processing time and resource usage.

Securing Your Spring Boot 3.x APIs Against Unrestricted Resource Consumption

Let’s explore practical steps to mitigate unrestricted resource consumption in your Spring Boot APIs.

Step 1: Implement Rate Limiting

Rate limiting restricts the number of requests a client can make within a specified time frame. One way to implement rate limiting is by using the Bucket4j library.

Add Dependency:

<!-- pom.xml -->
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>8.3.0</version>
</dependency>

Create a Rate Limiting Filter:

import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Refill;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class RateLimitingFilter extends OncePerRequestFilter {

private final Bucket bucket;

public RateLimitingFilter() {
Bandwidth limit = Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(1)));
this.bucket = Bucket.builder().addLimit(limit).build();
}

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (bucket.tryConsume(1)) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write("Too many requests");
}
}
}

Register the Filter:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RateLimitingConfig {

@Autowired
private RateLimitingFilter rateLimitingFilter;

@Bean
public FilterRegistrationBean<RateLimitingFilter> rateLimitingFilterBean() {
FilterRegistrationBean<RateLimitingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(rateLimitingFilter);
registrationBean.addUrlPatterns("/api/*"); // Apply to your API endpoints
return registrationBean;
}
}

Explanation:

  • Bucket4j: A Java rate-limiting library based on token buckets.
  • Bandwidth: Defines the rate limit, e.g., 100 requests per minute.
  • Filter: Applies rate limiting to incoming requests.

Step 2: Limit Payload Sizes

Restrict the size of incoming requests to prevent large payloads from consuming resources.

Configure Maximum Request Size:

# application.yml
spring:
servlet:
multipart:
max-file-size: 5MB
max-request-size: 5MB

For Non-Multipart Requests:

import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TomcatConfig {

@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(connector ->
connector.setMaxPostSize(5 * 1024 * 1024)); // 5 MB
return tomcat;
}
}

Explanation:

  • max-file-size and max-request-size: Limit multipart/form-data requests.
  • setMaxPostSize: Limits the size of HTTP POST requests.

Step 3: Implement Pagination and Data Limits

Avoid returning large datasets in a single response by implementing pagination.

Controller Method with Pagination:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

@GetMapping
public Page<User> getUsers(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
int maxSize = Math.min(size, 50); // Limit page size to 50
return userService.getUsers(PageRequest.of(page, maxSize));
}
}

Explanation:

  • PageRequest: Specifies the page number and size.
  • Math.min(size, 50): Ensures the page size does not exceed 50.

Service Layer:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

// Assume userRepository is already injected

public Page<User> getUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
}

Step 4: Set Timeouts and Execution Limits

Prevent long-running requests from consuming server resources indefinitely.

Configure Server Timeouts:

# application.yml
server:
shutdown: graceful
tomcat:
connection-timeout: 30s
max-threads: 200
min-spare-threads: 10
servlet:
session:
timeout: 30m

Explanation:

  • connection-timeout: Closes inactive connections after 30 seconds.
  • max-threads and min-spare-threads: Manages thread pool sizes.
  • session.timeout: Sets session expiration.

Set Read and Write Timeouts:

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setReadTimeout(Duration.ofSeconds(10))
.setConnectTimeout(Duration.ofSeconds(5))
.build();
}
}

Step 5: Control File Uploads

Limit file uploads by size and type to prevent disk space exhaustion.

Restrict File Types and Sizes:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/files")
public class FileController {

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFile(@RequestPart MultipartFile file) throws IOException {
if (file.getSize() > 5 * 1024 * 1024) { // Limit file size to 5 MB
throw new IllegalArgumentException("File size exceeds limit");
}
if (!file.getContentType().equals(MediaType.APPLICATION_PDF_VALUE)) {
throw new IllegalArgumentException("Only PDF files are allowed");
}
// Proceed with file processing
return "File uploaded successfully";
}
}

Explanation:

  • File Size Check: Validates that the file size does not exceed the limit.
  • Content Type Check: Ensures only allowed file types are uploaded.

Step 6: Monitor and Alert

Implement monitoring to detect and respond to resource consumption issues.

Use Actuator Endpoints:

<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Expose Relevant Endpoints:

# application.yml
management:
endpoints:
web:
exposure:
include: health, metrics, threaddump, httptrace

Set Up Alerts:

  • Integrate with Monitoring Tools: Use tools like Prometheus and Grafana to visualize metrics.
  • Configure Alerts: Set thresholds for resource usage and configure notifications.

Example Metrics to Monitor:

  • CPU and Memory Usage: Detect high resource consumption.
  • Thread Pool Stats: Monitor active threads and queue sizes.
  • Request Rates: Identify spikes in incoming requests.

Need help with Spring Framework? Master Spring TER, a ChatGPT model, offers real-time troubleshooting, problem-solving, and up-to-date Spring Boot info. Click master-spring-ter for free expert support!

Additional Best Practices

Use Caching Wisely

Implement caching to reduce load on backend services.

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

@Cacheable("products")
public Product getProductById(Long id) {
// Fetch product from database
}
}

Apply Bulkhead Patterns

Use resilience patterns to isolate resources.

Example with Resilience4j:

<!-- pom.xml -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
</dependency>
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.springframework.stereotype.Service;

@Service
public class ExternalService {

@Bulkhead(name = "externalService", type = Bulkhead.Type.SEMAPHORE)
public String callExternalApi() {
// Call external API
}
}

Configure Bulkhead Limits:

resilience4j:
bulkhead:
instances:
externalService:
maxConcurrentCalls: 10
maxWaitDuration: 500ms

Secure Your APIs

Implement authentication and authorization to prevent unauthorized access.

  • Use OAuth2 or JWTs: Secure your endpoints with token-based authentication.
  • Implement Role-Based Access Control (RBAC): Ensure users can only access permitted resources.

Conclusion

Unrestricted Resource Consumption is a critical vulnerability that can lead to Denial of Service attacks and resource exhaustion. By proactively implementing rate limiting, payload size restrictions, pagination, timeouts, and monitoring, you can significantly reduce the risk of such attacks on your Spring Boot 3.x APIs.

Remember, security is an ongoing process. Regularly review and update your application’s security measures to protect against evolving threats. By following best practices and leveraging the robust features of Spring Boot and Spring Security, you can build resilient APIs that provide a secure and reliable experience for your users.

References

By implementing these strategies, you not only safeguard your application against resource-based attacks but also ensure optimal performance and reliability for your users.

Sign up to discover human stories that deepen your understanding of the world.

Master Spring Ter
Master Spring Ter

Written by Master Spring Ter

https://chatgpt.com/g/g-dHq8Bxx92-master-spring-ter Specialized ChatGPT expert in Spring Boot, offering insights and guidance for developers.

No responses yet

Write a response