Brute Force Attack in Spring Boot with Basic Auth
Brute Force Attack in Spring Boot with Basic Auth — how this specific combination creates or exposes the vulnerability
A brute force attack against a Spring Boot application using HTTP Basic Authentication attempts to discover valid credentials by systematically submitting many username and password combinations. Because Basic Auth encodes credentials in Base64 without inherent confidentiality, each request reveals the username in plaintext and provides a predictable surface for guessing. In Spring Boot, developers commonly secure endpoints with HttpSecurity and form‑based or stateless Basic configurations. When rate limiting is absent or weak, an unauthenticated attacker can send many requests to the same protected endpoint, and the server responds with 401 until credentials are found. This behavior is especially risky when endpoints are publicly reachable and no additional protections, such as account lockout or progressive delays, are in place.
The attack surface is expanded when the API also exposes an OpenAPI/Swagger specification, because the spec may document the protected resource and hint at valid usernames or roles. Tools performing black-box scans can enumerate paths and observe differences in response codes or timing to infer valid credentials. Spring Boot applications that do not enforce strict request limits per IP or per authenticated context effectively allow parallel guesses, making automated credential spraying practical. Even when credentials are rotated, the absence of rate controls means attackers can resume guessing immediately.
Because middleBrick scans the unauthenticated attack surface, it may detect endpoints that return 401 or 403 responses and flag the absence of rate limiting as a finding. The scan runs 12 parallel checks, including Rate Limiting and Authentication, correlating the presence of Basic Auth with missing throttling mechanisms. This combination highlights configurations where credentials are repeatedly challenged without friction, aiding attackers who leverage scripts or distributed requests to evade simple IP-based counters.
Basic Auth-Specific Remediation in Spring Boot — concrete code fixes
To mitigate brute force risks with HTTP Basic Auth in Spring Boot, apply server‑side rate limiting, strengthen credential storage, and reduce information leakage in authentication responses. Below are concrete, working configurations and code examples.
1. Rate limiting with Spring Security and a token‑bucket filter
Add a custom filter before the Spring Security filter chain to enforce request limits per username or IP. This example uses a simple in‑memory rate limiter; in production, consider a distributed store like Redis for clustered deployments.
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
private final int maxRequests = 5;
private final Duration window = Duration.ofMinutes(1);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String key = request.getRemoteAddr();
if (request.getHeader("Authorization") != null && request.getHeader("Authorization").startsWith("Basic ")) {
String auth = request.getHeader("Authorization").substring("Basic ".length()).trim();
String decoded = new String(Base64.getDecoder().decode(auth), StandardCharsets.UTF_8);
String[] parts = decoded.split(":", 2);
if (parts.length == 2) {
key = parts[0]; // rate limit per username
}
}
RateLimiter limiter = limiters.computeIfAbsent(key, k -> RateLimiter.create(maxRequests / window.toSeconds()));
if (limiter.tryAcquire()) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("Rate limit exceeded");
}
}
}
Register the filter before Spring Security’s filter chain:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, RateLimitFilter rateLimitFilter) throws Exception {
http.addFilterBefore(rateLimitFilter, UsernamePasswordAuthenticationFilter.class);
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
2. Secure credential storage and prevent enumeration
Store passwords using a strong adaptive hash such as bcrypt. Avoid returning detailed error messages that reveal whether a username exists. Configure Spring Security to use a standard UserDetailsService and ensure the authentication entry point does not disclose internal details.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
UserDetails user = User.withUsername("alice")
.password(encoder.encode("S3cureP@ss"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.exceptionHandling(ex -> ex.authenticationEntryPoint(
(request, response, authException) -> {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("Unauthorized");
}));
return http.build();
}
}
3. Additional hardening
Use HTTPS to protect credentials in transit, rotate credentials regularly, and consider combining Basic Auth with additional factors where feasible. middleBrick’s scans can validate that these configurations are reflected in the runtime behavior and that endpoints do not leak information that facilitates guessing.