
- Sprint Security - Home
- Spring Security - Introduction
- Spring Security - Architecture
- Spring Security - Project Modules
- Spring Security - Environment Setup
- Spring Security - Form Login
- Spring Security - Custom Form Login
- Spring Security - Logout
- Spring Security - Remember Me
- Spring Security - Redirection
- Spring Security - Taglibs
- Spring Security - XML Configuration
- Spring Security - Authentication Provider
- Spring Security - Basic Authentication
- Spring Security - AuthenticationFailureHandler
- Spring Security - JWT
- Spring Security - Retrieve User Information
- Spring Security - Maven
- Spring Security - Default Password Encoder
- Spring Security – Password Encoding
- Spring Security - Methods Level
- Spring Security - Manual Authentication
- Spring Security - Extra Login Fields
- Spring Security - Prevent Brute Force
- Spring Security - Login Page with React
- Spring Security - Security Filter Chain
- Spring Security - Securing Spring Boot API
- Spring Security Useful Resources
- Spring Security - Quick Guide
- Spring Security - Useful Resources
- Spring Security - Discussion
Spring Security - Prevent Brute Force
Brute force attacks are one of the most common and simple types of attacks against web applications. They occur when an attacker tries every possible combination of inputs to guess credentials, such as usernames and passwords. For modern web applications, its crucial to implement security measures that prevent or mitigate brute force attempts.
Spring Security, a powerful and flexible framework for securing Spring-based applications, can be used to prevent brute force attacks. In this article, we'll explore various techniques for mitigating brute force attacks using Spring Security, providing you with code examples and best practices.
Introduction to Brute Force Attacks
What are Brute Force Attacks?
A brute force attack occurs when an attacker attempts to gain unauthorized access to a system by systematically trying all possible combinations of passwords or other credentials. The attacker might use automated tools to speed up the process. These attacks can target login pages, account creation forms, or API endpoints.
Why Preventing Brute Force Attacks is Critical
Brute force attacks can lead to data breaches, service disruptions, and even account hijacking. If a users credentials are compromised, attackers can gain unauthorized access to sensitive information, potentially harming both individuals and organizations.
Overview of Spring Security
What is Spring Security?
Spring Security is a comprehensive security framework for Java applications, providing authentication, authorization, and protection against common vulnerabilities. It is a powerful tool for securing web applications, and one of its key features is flexibility in configuring security protocols.
Key Concepts in Spring Security
Authentication− The process of verifying the identity of a user (e.g., via username and password).
Authorization− Determining whether a user has permission to perform an action (e.g., accessing a resource).
Session Management− Tracking user sessions to ensure that users are properly authenticated during their interaction with the application.
Filters− Components used to process incoming requests (e.g., for authentication, authorization, etc.).
Basic Brute Force Protection with Spring Security
Using Spring Security's Default Authentication Mechanism
Spring Security offers default authentication mechanisms such as form-based login and HTTP Basic authentication. While it provides a basic level of security, it doesnt directly protect against brute force attacks. To enhance security, we need to add additional mechanisms.
Adding Login Attempt Limiting
One of the simplest ways to prevent brute force attacks is to limit the number of failed login attempts. This can be achieved by tracking failed login attempts and temporarily locking accounts after a specified number of failed attempts. Heres a simple implementation−
SecurityConfig.java
package com.tutorialspoint.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .formLogin(form -> form .loginPage("/login") // Custom failure handler. See code below .failureHandler(new CustomAuthenticationFailureHandler()) .permitAll() ) .authorizeHttpRequests(auth -> auth .requestMatchers("/login", "/register").permitAll() .anyRequest().authenticated() ); return http.build(); } }
In this example, we customize the login page and handle authentication failures with a custom failure handler. To track failed login attempts, we can create a custom failure handler. This handler will record the number of failed attempts for a user and lock the account if the number exceeds a certain threshold.
CustomAuthenticationFailureHandler.java
package com.tutorialspoint.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import java.io.IOException; import com.tutorialspoint.service.LoginAttemptService; public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { private static final int MAX_FAILED_ATTEMPTS = 5; @Autowired private LoginAttemptService loginAttemptService; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { String username = request.getParameter("username"); // Track failed attempts for this username loginAttemptService.recordFailedAttempt(username); if (loginAttemptService.isAccountLocked(username)) { response.sendRedirect("/account-locked"); } else { response.sendRedirect("/login?error=true"); } } }
LoginAttemptService.java
package com.tutorialspoint.service; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Service public class LoginAttemptService { private static final int MAX_FAILED_ATTEMPTS = 5; private static final long LOCK_TIME_DURATION = 24 * 60 * 60 * 1000; // 24 hours private Map<String, Integer> attemptsCache = new ConcurrentHashMap<>(); private Map<String, Long> lockTimeCache = new HashMap<>(); public void recordFailedAttempt(String username) { int attempts = attemptsCache.getOrDefault(username, 0); attempts++; attemptsCache.put(username, attempts); if (attempts >= MAX_FAILED_ATTEMPTS) { lockTimeCache.put(username, System.currentTimeMillis()); } } public boolean isAccountLocked(String username) { if (!lockTimeCache.containsKey(username)) { return false; } long lockTime = lockTimeCache.get(username); if (System.currentTimeMillis() - lockTime < LOCK_TIME_DURATION) { return true; } else { // Unlock the account after the lock time duration lockTimeCache.remove(username); attemptsCache.remove(username); return false; } } public void resetAttempts(String username) { attemptsCache.remove(username); lockTimeCache.remove(username); } }
Rate Limiting
Rate limiting is a key strategy to prevent brute force attacks. By limiting the number of requests a user can make within a specific period, we can prevent an attacker from trying every possible password combination.
SecurityConfigRateLimit.java
package com.tutorialspoint.config; import com.example.RateLimitingFilter; import com.example.RateLimiter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity public class SecurityConfigRateLimit { private final RateLimiter rateLimiter = new RateLimiter(); @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .addFilterBefore(new RateLimitingFilter(rateLimiter), UsernamePasswordAuthenticationFilter.class) .formLogin(form -> form.permitAll()); return http.build(); } }
RateLimitingFilter.java
package com.tutorialspoint.config; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; public class RateLimitingFilter extends OncePerRequestFilter { private final RateLimiter rateLimiter; public RateLimitingFilter(RateLimiter rateLimiter) { this.rateLimiter = rateLimiter; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String ipAddress = request.getRemoteAddr(); if (!rateLimiter.consume(ipAddress)) { response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), "Too many requests"); return; } filterChain.doFilter(request, response); } }
RateLimiter.java
package com.tutorialspoint.config; import org.springframework.stereotype.Service; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @Service public class RateLimiter { private static final int MAX_REQUESTS_PER_MINUTE = 60; private static final long ONE_MINUTE_IN_MILLIS = TimeUnit.MINUTES.toMillis(1); private final ConcurrentHashMap<String, RequestInfo> requestCounts = new ConcurrentHashMap<>(); public boolean consume(String ipAddress) { long currentTime = System.currentTimeMillis(); RequestInfo requestInfo = requestCounts.computeIfAbsent(ipAddress, key -> new RequestInfo()); synchronized (requestInfo) { if (currentTime - requestInfo.timestamp > ONE_MINUTE_IN_MILLIS) { requestInfo.timestamp = currentTime; requestInfo.requestCount.set(0); } if (requestInfo.requestCount.incrementAndGet() > MAX_REQUESTS_PER_MINUTE) { return false; } } return true; } private static class RequestInfo { long timestamp; AtomicInteger requestCount = new AtomicInteger(0); } }
Output
Login Page
Username− user
Password− password

On Successful Login

On Failed Attempts
After 5 failed login attempts ( for user, type user / password, anything but password)

Conclusion
Preventing brute force attacks is crucial for ensuring the security of your web applications. By leveraging Spring Securitys powerful features such as rate limiting, account lockout, custom filters you can effectively mitigate brute force attempts. Additionally, integrating two-factor authentication (2FA) and strong session management can provide further layers of protection against unauthorized access.
By adopting these strategies, you'll help protect your users from the dangers of brute force attacks and enhance the overall security of your application.