Api Rate Abuse in Spring Boot
How Api Rate Abuse Manifests in Spring Boot
Rate abuse in Spring Boot applications often exploits the framework's default permissive configurations and common development patterns. When Spring Boot developers use @RestController endpoints without rate limiting, attackers can flood endpoints with requests, exhausting server resources and potentially causing denial of service.
A classic Spring Boot vulnerability occurs when authentication endpoints lack rate limiting. Consider this common pattern:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
// No rate limiting - vulnerable to credential stuffing
User user = userService.findByUsername(request.getUsername());
if (user != null && passwordEncoder.matches(request.getPassword(), user.getPassword())) {
return ResponseEntity.ok(new AuthResponse(jwtProvider.generateToken(user)));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
This endpoint allows unlimited login attempts, enabling brute force attacks. Attackers can automate credential stuffing at scale, testing thousands of username/password combinations per minute.
Another Spring Boot-specific pattern involves Spring Data JPA repositories without pagination limits. A vulnerable endpoint might look like:
@GetMapping("/users")
public Page<User> getAllUsers(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "1000") int size) {
// No maximum size limit - allows massive data retrieval
return userRepository.findAll(PageRequest.of(page, size));
}
Here, an attacker can request enormous page sizes (1000, 10000, or more), causing memory exhaustion and database performance degradation. The default Spring Boot configuration doesn't enforce reasonable limits on collection sizes.
Spring Boot Actuator endpoints present another attack surface when exposed without protection. By default, /actuator/health and /actuator/metrics are accessible, but developers often enable additional endpoints like /actuator/env or /actuator/configprops without authentication:
@Configuration
public class ActuatorConfig {
@Bean
public EndpointWebMvcManagementContextConfiguration actuatorConfig() {
return new EndpointWebMvcManagementContextConfiguration();
}
}
Attackers can repeatedly poll these endpoints to gather system information, potentially discovering sensitive configuration data or application structure details that aid further attacks.
Spring Boot-Specific Detection
Detecting rate abuse vulnerabilities in Spring Boot requires examining both code patterns and runtime behavior. Static analysis can identify missing rate limiting annotations and unbounded collection endpoints.
middleBrick's Spring Boot-specific detection identifies several key patterns:
Scanning Spring Boot endpoint: https://api.example.com/user/login
• Authentication endpoint without rate limiting
• Risk: High - vulnerable to credential stuffing
• Recommendation: Add @RateLimit or implement Spring Security rate limiting
Scanning Spring Boot endpoint: https://api.example.com/users
• Collection endpoint with unlimited page size
• Risk: Medium - potential for resource exhaustion
• Recommendation: Set maximum page size (e.g., 100 items)
Scanning Spring Boot endpoint: https://api.example.com/actuator/health
• Actuator endpoint accessible without authentication
• Risk: Low - information disclosure potential
• Recommendation: Secure actuator endpoints with Spring Security
middleBrick tests rate abuse by sending rapid, repeated requests to identify endpoints that lack rate limiting. The scanner analyzes Spring Boot's default configurations and common vulnerable patterns specific to the framework.
Runtime detection involves monitoring for:
- Excessive requests from single IP addresses to authentication endpoints
- Large collection requests that could exhaust memory
- Repeated actuator endpoint polling
- Unusual request patterns that deviate from normal usage
Spring Boot's Actuator provides metrics that help identify rate abuse patterns:
@RestController
@RequestMapping("/api")
public class MetricsController {
@GetMapping("/metrics/rate-abuse-detection")
public RateAbuseMetrics getRateAbuseMetrics() {
// Monitor request rates per endpoint
Map<String, Long> requestCounts = requestCounter.getEndpointCounts();
// Identify anomalies: sudden spikes, sustained high rates
return new RateAbuseMetrics(requestCounts, detectAnomalies(requestCounts));
}
}
These metrics can trigger alerts when abuse patterns are detected, allowing for automated rate limiting or blocking of abusive clients.
Spring Boot-Specific Remediation
Spring Boot provides several native approaches to mitigate rate abuse vulnerabilities. The most effective solution combines Spring Security with rate limiting filters.
For authentication endpoint protection, implement Spring Security's built-in rate limiting:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").fullyAuthenticated()
.requestMatchers("/api/**").permitAll()
.requestMatchers("/actuator/**").hasRole("ADMIN")
)
.addFilterBefore(rateLimitFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public RateLimitFilter rateLimitFilter() {
return new RateLimitFilter(
new InMemoryRateLimiter(
Map.of(
"/api/auth/login", RateLimit.of(5, Duration.ofMinutes(1)),
"/api/users", RateLimit.of(100, Duration.ofMinutes(1))
)
)
);
}
}
This configuration limits login attempts to 5 per minute per IP address, effectively preventing credential stuffing attacks while allowing legitimate users to authenticate.
For collection endpoints, implement pagination limits using Spring Data's Pageable interface:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public Page<User> getUsers(@PageableDefault(size = 50, sort = "id") Pageable pageable) {
// Maximum page size enforced by @PageableDefault
Pageable limitedPageable = PageRequest.of(
pageable.getPageNumber(),
Math.min(pageable.getPageSize(), 100) // Cap at 100 items
);
return userRepository.findAll(limitedPageable);
}
@GetMapping("/search")
public Page<User> searchUsers(@RequestParam String query,
@PageableDefault(size = 50) Pageable pageable) {
// Apply both pagination and query limits
if (query.length() > 100) {
throw new IllegalArgumentException("Query too long");
}
return userRepository.findByUsernameContaining(query, pageable);
}
}
This approach ensures no single request can retrieve more than 100 users, preventing memory exhaustion attacks while still providing reasonable pagination for legitimate use.
For Actuator endpoint protection, secure sensitive endpoints with Spring Security:
@Configuration
public class ActuatorSecurityConfig {
@Bean
public EndpointFilter<Endpoint> actuatorSecurityFilter() {
return (endpoint, request) -> {
String path = request.getRequestURI();
if (path.startsWith("/actuator/") &&
!path.equals("/actuator/health") &&
!path.equals("/actuator/metrics")) {
// Require admin role for sensitive endpoints
return request.isUserInRole("ADMIN");
}
return true; // Allow public endpoints
}; }
}
This configuration allows public access to health and metrics endpoints while requiring authentication for sensitive actuator endpoints like /actuator/env or /actuator/configprops.