Block Malicious API Requests with Rate Limiting

The Malicious user is trying to hack services. There is also a computer with rate limiting to prevent the attack.

Rate limiting is a technique used in computer systems to control the speed at which requests or actions can be made within a specific timeframe. It limits the number of requests occurring within a given period, preventing abuse or overload. It’s like a traffic signal on the internet, ensuring a smooth and controlled data flow.

Rate limiting prevents excessive or malicious usage by enforcing a maximum limit on the number of actions that can happen within a specific time window. For example, it can restrict the number of login attempts a user can make within a minute or limit the number of API requests a program can send per hour. By implementing rate limiting, system administrators can protect their resources, maintain system stability, and promote fair usage of their services.

Throughout my career, the situation of malicious users attacking our services has already happened, performing a massive amount of requests and making the service very slow. To solve this problem, we use rate limiting. So, rate limiting is a fundamental concept to remember when developing Microservices and exposing APIs.

Rate Limiting Use Cases

Rate limiting is beneficial in various situations where controlling traffic flow or actions within a system is necessary. Here are some common scenarios where rate limiting is advantageous:

API Management: When exposing an API to external users or third-party developers, rate limiting helps ensure fair usage and prevent abuse. It allows you to restrict the number of requests that can be made in a given period, preventing one user or application from overwhelming the API and affecting its performance or availability. It’s even possible to prevent users from certain regions to not access the API with a more strict rate limiting rule.

Security and Protection: Rate limiting can effectively protect against brute-force attacks or other malicious activities. For example, the Denial-of-Service (DoS) attack which is when an attacker overwhelms a target system with excessive traffic or requests, making it unavailable to legitimate users. Using rate limiting, we limit the number of login attempts within a specific time frame, making it harder for attackers to guess passwords or gain unauthorized access.

System Stability: Rate limiting helps maintain system stability and prevents overload. Controlling the rate of incoming requests or actions ensures that the system resources are not overwhelmed and can handle the load effectively. This is particularly relevant for high-traffic websites or applications where a sudden influx of requests can cause performance degradation or downtime.

Resource Conservation: Rate limiting can be used to conserve resources, especially in cases where limited resources are allocated to each user or application. By setting a limit on the number of actions or requests, you can prevent excessive use of resources and ensure fair distribution among users.

Monetization and Subscription Models: If you offer services through a subscription model or have different tiers of access, rate limiting can be used to enforce usage limits based on the subscription level. This helps control access to features or restrict usage based on the subscription plan, ensuring that users adhere to their allotted usage quotas.

In summary, rate limiting is useful in scenarios involving API management, security protection, system stability, resource conservation, and enforcing usage limits for monetization or subscription models. It helps maintain fairness, protect against abuse, and ensure the efficient use of system resources.

Rate Limiting Example with Spring

Now, let’s see an example of rate-limiting with the most popular Java frameworks, Spring!

1 – Let’s declare the Spring dependencies into the source code.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2 – We create now a filter class that intercepts every request, then we create a Queue with the ipAddress and will block the IP if more than 100 requests are made within 5 minutes. In case the rate limiting condition is met, we will send the HttpStatus “too many requests” to the user which is the code 429:

public class RateLimitFilter extends OncePerRequestFilter {

    private static final int MAX_REQUESTS = 100;
    private static final Duration TIME_WINDOW = Duration.ofMinutes(5);
    private static final Map<String, Queue<Long>> requestMap = new ConcurrentHashMap<>();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String ipAddress = request.getRemoteAddr();
        Queue<Long> requestQueue = requestMap.computeIfAbsent(ipAddress, k -> new ConcurrentLinkedQueue<>());

        long now = System.currentTimeMillis();
        requestQueue.add(now);

        while (!requestQueue.isEmpty() && now - requestQueue.peek() > TIME_WINDOW.toMillis()) {
            requestQueue.poll();
        }

        if (requestQueue.size() <= MAX_REQUESTS) {
            filterChain.doFilter(request, response);
        } else {
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            response.getWriter().write("Rate limit exceeded");
        }
    }
}

3 – In the basic web configuration from Spring, we include our RateLimitFilter class as a filter. The method addFilterAfter will intercept every HTTP request, will execute the RateLimitFilter logic and will invoke the API method only if the rate limiting is not exceeded.

@Configuration
@EnableWebSecurity
public class RateLimitingConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/api/**")
            .permitAll()
            .and()
            .addFilterAfter(rateLimitFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public RateLimitFilter rateLimitFilter() {
        return new RateLimitFilter();
    }
}

4 – Finally, by accessing the following API more than 100 times within the time frame of 5 minutes, the “too many requests” HTTP status will be returned and will block requests of the user IP:

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/resource")
    @PreAuthorize("hasRole('USER')")
    public String getResource() {
        // Your API logic here
    }
}

Conclusion

Rate limiting is a crucial concept to keep in mind when developing Microservices. Ideally, we shouldn’t wait an attack to use rate limiting, we should make our Microservices API prepared to avoid attacks such DoS (Denial-of-Service). Therefore, let’s see the key points of rate limiting:

  • Rate limiting is a technique used to control and limit the number of requests or actions that can be performed within a specific timeframe.
  • It helps prevent abuse, misuse, and overloading of systems or APIs by limiting the rate at which requests can be made.
  • Rate limiting can protect against various issues, including DoS attacks, brute force attacks, and excessive resource consumption.
  • It sets thresholds on the number of requests allowed per user, IP address, or other identifiers, within a defined time window.
  • When the rate limit is exceeded, further requests may be rejected or delayed, or an error response may be returned.
  • Rate limiting can be implemented at different levels, such as network level, server level, or application level.
  • It is commonly used in web applications, APIs, and microservices to ensure fair usage, maintain system stability, and protect against abuse or unauthorized access.
  • Rate limiting strategies can include fixed quotas, sliding windows, token buckets, or leaky buckets, depending on the specific requirements and use cases.
  • Proper configuration and tuning of rate limiting parameters are crucial to balance security, user experience, and system performance.
  • Rate limiting can be combined with other security measures, such as authentication, authorization, and anomaly detection, for comprehensive protection against various threats.
Written by
Rafael del Nero
Join the discussion

Stand Out as a Java Developer and Get Your Dream Job!

You will get the book by email in a few minutes after registering.