Brute Force Attack in Spring Boot
How Brute Force Attack Manifests in Spring Boot
In Spring Boot applications, brute force attacks typically target authentication endpoints like /login, /authenticate, or OAuth2 token endpoints. Attackers automate rapid credential stuffing or password guessing to exploit weak authentication controls. Spring Boot's convention-over-configuration nature can inadvertently expose these risks if developers rely solely on Spring Security's defaults without additional hardening.
Common Attack Patterns:
- Credential Stuffing: Using breached username/password pairs against
/login(often aUsernamePasswordAuthenticationFilterendpoint). - Password Guessing: Systematic attempts with common passwords or dictionary words against form-login or basic-auth endpoints.
- Token Brute Force: Guessing JWT signatures, OAuth2
client_id/client_secret, or API keys in authorization headers.
Spring Boot-Specific Vulnerabilities:
- Missing rate limiting on authentication endpoints allows unlimited attempts.
- Default
DaoAuthenticationProviderwithout account lockout policies. - Exposing sensitive error messages (e.g.,
Bad credentials) that confirm valid usernames. - Stateless JWT authentication without tracking failed attempts per user/IP.
For example, a typical vulnerable Spring Boot login controller without throttling:
@RestController
public class AuthController {
@PostMapping("/login")
public ResponseEntity> login(@RequestBody LoginRequest request) {
// Authentication handled by Spring Security filter chain
return ResponseEntity.ok("Authenticated");
}
}
// Security config with no rate limiting
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
return http.build();
}
}Here, an attacker can send unlimited POST requests to /login with different passwords. Spring Security will process each attempt against the UserDetailsService without delay or lockout.
Spring Boot-Specific Detection
Detecting brute force vulnerabilities in Spring Boot APIs requires testing authentication endpoints for absence of throttling mechanisms. middleBrick's rate limiting check actively probes these endpoints by sending rapid sequences of authentication attempts (e.g., 20–30 requests in quick succession) with invalid credentials. The scanner evaluates responses for HTTP 429 (Too Many Requests), increasing latency, or consistent 200/401 status codes indicating no rate limiting.
What middleBrick Looks For:
- No
Retry-Afterheader orX-RateLimit-*headers in responses. - Identical response times and status codes across rapid requests.
- Lack of account lockout triggers after repeated failures (if user enumeration is possible).
Example Scan Output (middleBrick Report):
Check: Rate Limiting
Status: FAIL
Severity: High
Endpoint: POST /api/v1/login
Details: No rate limiting detected. Sent 30 requests in 5 seconds, all returned 401 in ~120ms. No 429 responses or latency increases observed.
Remediation: Implement IP-based or user-based rate limiting using Bucket4j or Spring Cloud Gateway.
Compliance: OWASP API Top 10 A07:2021, PCI-DSS 8.6.2Manual Verification: You can replicate middleBrick's test using curl in a loop:
for i in {1..30}; do curl -X POST http://api.example.com/login -d '{"username":"admin","password":"wrong"}' -H 'Content-Type: application/json' -s -o /dev/null -w '%{http_code} '; doneIf all responses are 401 with no 429, rate limiting is missing.
Spring Boot-Specific Remediation
Remediation in Spring Boot involves implementing rate limiting and account lockout at the filter or controller level. Use battle-tested libraries like Bucket4j (in-memory or Redis-backed) integrated via Spring Security's filter chain. Avoid custom in-memory counters that fail in distributed deployments.
Step 1: Add Bucket4j Dependency
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>buck4j-core</artifactId>
<version>8.10.0</version>
</dependency>
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j-redis</artifactId>
<version>8.10.0</version>
</dependency>Step 2: Configure Rate Limiter Filter
@Component
public class RateLimitFilter extends OncePerRequestFilter {
@Value("${rate.limit.bandwidth:10}")
private int bandwidth;
@Value("${rate.limit.duration:1}")
private int duration;
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
private Bucket resolveBucket(String key) {
return buckets.computeIfAbsent(key, k -> {
Bandwidth limit = Bandwidth.classic(bandwidth, Refill.greedy(bandwidth, Duration.ofMinutes(duration)));
return Bucket.builder()
.addLimit(limit)
.build();
});
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String ip = request.getRemoteAddr();
String path = request.getRequestURI();
// Apply rate limit only to login endpoint
if (path.equals("/api/v1/login") && request.getMethod().equals("POST")) {
Bucket bucket = resolveBucket(ip);
if (bucket.tryConsume(1)) {
chain.doFilter(request, response);
} else {
response.setStatus(429);
response.getWriter().write("Too many requests. Please try again later.");
}
return;
}
chain.doFilter(request, response);
}
}Step 3: Integrate with Spring Security
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private RateLimitFilter rateLimitFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilterBefore(rateLimitFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/v1/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/api/v1/login");
return http.build();
}
// Also enforce strong password encoding
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
}Production Considerations:
- Use Redis-backed Bucket4j for distributed systems to synchronize limits across instances.
- Combine IP-based limits with username-based limits to prevent targeted user locking.
- Set reasonable thresholds (e.g., 5–10 attempts per minute per IP).
- Log blocked attempts for security monitoring.
With this configuration, Spring Boot will respond with 429 Too Many Requests after exceeding the threshold, effectively mitigating brute force attacks.
Continuous Monitoring with middleBrick
While code fixes are essential, brute force protections can degrade over time due to configuration drift or new endpoint additions. middleBrick's continuous monitoring (available in Pro and Enterprise tiers) automatically re-scans your APIs on a schedule, alerting you if rate limiting is removed or misconfigured. For example, if a developer accidentally removes the RateLimitFilter bean, middleBrick will detect the regression and notify your team via Slack or email, ensuring consistent security posture.
Integrate middleBrick into your CI/CD pipeline with the GitHub Action to catch missing rate limits before deployment:
# .github/workflows/api-security.yml
name: API Security Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Run middleBrick scan
uses: middlebrick/github-action@v1
with:
api-url: ${{ secrets.STAGING_API_URL }}
fail-below-score: 80 # Fail PR if security score drops below BThis prevents brute force vulnerabilities from reaching production.