Rate Limiting Filter For Login

I’m trying to implement a Filter using bucket4j to limit the amount of login attempts (intercepted using Burp suite and perform a BRUTE Force Attack) made by an IP address as follows:

@Component
public class RateLimitingFilter extends OncePerRequestFilter {

private final ConcurrentHashMap<String, Bucket> ipBuckets = new ConcurrentHashMap<>();
private static final int REQUEST_THRESHOLD = 20;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String requestURI = request.getRequestURI();

  if ("POST".equalsIgnoreCase(request.getMethod()) && request.getQueryString() != null
  		&& request.getQueryString().contains("v-r=uidl")&&
  		"/login".equals(requestURI)) {
  	
  	String clientIP = request.getRemoteAddr();
  	Bucket bucket = ipBuckets.computeIfAbsent(clientIP, this::createBucket);

  	if (bucket.tryConsume(1)) {
  		filterChain.doFilter(request, response);
  	} else {
  		response.setStatus(429);
  		response.getWriter().write("Too many requests. Please try again later.");
  		System.out.println("Rate limit exceeded for IP: " + clientIP);
  		return;
  	}
  } else {
  	filterChain.doFilter(request, response);
  }

}

private Bucket createBucket(String key) {
Refill refill = Refill.intervally(REQUEST_THRESHOLD, Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(REQUEST_THRESHOLD, refill);
return Bucket4j.builder().addLimit(limit).build();
}

}

The above mechanism applies the filter for all requests which I don’t need. I can’t differentiate between a login POST request with other requests. request.getRequestURI() always returns “/”. How can i get the actual URI which is “/login”? Any suggestions?

Wouldn’t it be way easier and technology-agnostic to hook yourself into UserDetailsService and fail-fast once some of your criteria are broken?

Thanks for the reply. Yes, what you suggested would have been a better choice but the use case is: Security Audit Team runs Burp suite to intercept login requests and perform an intruder attack. In Vaadin’s case, only one request is processed but burp suite gets a HTTP response 200. Hence, security audit team thinks response was a successful response.

Hence, the need of the above Filter, in which after a few requests, it gives a 429 status code.

Sounds like a false positive in the Burp Suite Test which should be fixed by the security team in my opinion otherwise it could lead to a false sense of security.

Spoiler: All internal requests of Vaadin go through the same requestURI - so it’s impossible to filter those by /login ;)

" it could lead to a false sense of security" I agree. Thanks anyway…