Missing Rate Limiting
Missing Rate Limiting allows attackers to abuse API endpoints without request throttling, enabling automated attacks like credential stuffing, data scraping, and resource exhaustion that can overwhelm services and compromise security at scale.
What Is Missing Rate Limiting?
Missing Rate Limiting occurs when an application fails to restrict the number of requests a user or client can make to an API endpoint within a given timeframe. Without proper throttling mechanisms, attackers can send unlimited requests to exploit functionality, extract data, or exhaust system resources. This vulnerability transforms legitimate API features into attack vectors that can be abused at machine speed and scale.
The fundamental problem is that APIs are designed to be accessible and responsive, but without rate limiting, there is no distinction between legitimate use and malicious abuse. An attacker can write a simple script to make thousands or millions of requests in minutes, overwhelming authentication systems, scraping entire databases, or causing denial of service. What might take a human hours or days to accomplish manually can be automated and executed in seconds.
Rate limiting vulnerabilities are particularly dangerous in modern microservices architectures where APIs are the primary interface for communication. According to the OWASP API Security Top 10, this vulnerability is ranked as API4:2023, reflecting its prevalence in production systems. The lack of rate limiting often goes unnoticed during development and testing because legitimate usage patterns rarely trigger the thresholds that would expose the vulnerability.
How It Works
The attacker discovers an API endpoint that performs a valuable or resource-intensive operation—such as authentication, data retrieval, file upload, or report generation—and observes that there are no restrictions on request frequency.
The attacker creates a script or uses automated tools to send hundreds or thousands of requests per second to the target endpoint. This could be credential combinations for brute force, sequential IDs for data enumeration, or simply repeated requests to expensive operations.
Because the application lacks rate limiting controls, the server attempts to process every incoming request. Database queries are executed, authentication checks are performed, and resources are consumed for each request, regardless of volume or source.
Depending on the attacker's goal, they successfully brute force credentials, scrape sensitive data, enumerate user accounts, or exhaust system resources. The lack of throttling allows them to complete attacks that would be impossible with proper rate limiting in place.
The attack results in unauthorized access, mass data exfiltration, or denial of service for legitimate users. Server resources (CPU, memory, database connections) are exhausted, response times degrade, and the service may become unavailable or compromised.
Vulnerable Code Example
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
// VULNERABLE: No rate limiting on authentication endpoint
// Attacker can attempt unlimited login attempts for brute force
User user = userService.authenticate(req.getUsername(), req.getPassword());
if (user != null) {
String token = jwtService.generateToken(user);
return ResponseEntity.ok(Map.of("token", token));
}
return ResponseEntity.status(401).body("Invalid credentials");
}
@GetMapping("/users/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
// VULNERABLE: No rate limiting on data endpoints
// Attacker can enumerate all users by iterating through IDs
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
}
// An attacker can script unlimited requests:
// for (int i = 0; i < 1000000; i++) {
// POST /api/login with different passwords
// GET /api/users/i to scrape all user data
// }
// No throttling = unlimited attack speedSecure Code Example
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserService userService;
// In-memory rate limiter cache (use Redis for distributed systems)
private final Map<String, Bucket> cache = new ConcurrentHashMap<>();
private Bucket createBucket() {
// SECURE: Rate limit to 5 requests per minute per IP/user
Bandwidth limit = Bandwidth.classic(5, Refill.intervally(5, Duration.ofMinutes(1)));
return Bucket.builder().addLimit(limit).build();
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req, HttpServletRequest request) {
String clientId = request.getRemoteAddr(); // Use IP or user ID
Bucket bucket = cache.computeIfAbsent(clientId, k -> createBucket());
// Try to consume 1 token from the bucket
if (bucket.tryConsume(1)) {
User user = userService.authenticate(req.getUsername(), req.getPassword());
if (user != null) {
String token = jwtService.generateToken(user);
return ResponseEntity.ok(Map.of("token", token));
}
return ResponseEntity.status(401).body("Invalid credentials");
}
// SECURE: Return 429 Too Many Requests when limit exceeded
return ResponseEntity.status(429)
.header("X-Rate-Limit-Retry-After-Seconds", "60")
.body("Too many requests. Please try again later.");
}
@GetMapping("/users/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id, HttpServletRequest request) {
String clientId = request.getRemoteAddr();
Bucket bucket = cache.computeIfAbsent(clientId, k -> createBucket());
if (bucket.tryConsume(1)) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
return ResponseEntity.status(429).body("Too many requests");
}
}
// With rate limiting, automated attacks are throttled:
// - 5 login attempts per minute maximum
// - Brute force attacks become impractical
// - Data scraping is severely limited
// - System resources are protectedTypes of Rate Limiting Abuse
API Endpoint Abuse
Unlimited requests to data retrieval endpoints enable mass data scraping and enumeration attacks. Attackers can iterate through sequential IDs, extract entire user databases, or scrape product catalogs by making thousands of requests per second. Public APIs without rate limiting are particularly vulnerable, as they are easily discoverable and accessible. This type of abuse can expose personally identifiable information (PII), business intelligence, or proprietary data at scale.
Authentication Endpoint Abuse
Login, password reset, and registration endpoints without rate limiting enable credential stuffing and brute force attacks. Attackers use lists of compromised credentials or password dictionaries to attempt thousands of login combinations automatically. Two-factor authentication (2FA) endpoints are also vulnerable, allowing attackers to brute force one-time passwords (OTPs). Even with strong password policies, unlimited authentication attempts eventually lead to successful account compromise.
Resource Exhaustion
Expensive operations like file uploads, report generation, complex searches, or database-intensive queries without rate limiting can be weaponized for denial of service (DoS). Attackers trigger resource-intensive operations repeatedly to consume CPU, memory, disk I/O, or database connections, degrading performance for legitimate users or causing complete service outages. Cloud environments are particularly vulnerable as this can result in massive unexpected costs due to auto-scaling and resource consumption.
Impact
The absence of rate limiting can lead to severe security and operational consequences. The impact extends beyond technical disruption to business reputation, compliance violations, and financial losses.
Attackers can test millions of compromised username/password combinations from data breaches against login endpoints. Without rate limiting, they can cycle through credential lists at high speed, successfully compromising accounts and gaining unauthorized access to user data, financial information, or administrative privileges.
Unlimited requests enable attackers to scrape entire databases by enumerating through sequential IDs or iterating through search parameters. This results in unauthorized access to sensitive personal information, intellectual property, or business data, leading to GDPR violations, regulatory fines, and loss of customer trust.
Resource exhaustion attacks can overwhelm servers, databases, and infrastructure, causing service degradation or complete outages. Legitimate users are unable to access the service, resulting in lost revenue, damaged reputation, and violated service level agreements (SLAs). For e-commerce platforms, even brief outages translate to significant financial losses.
In cloud environments with auto-scaling, unlimited API requests can trigger massive resource provisioning, leading to unexpected infrastructure costs. Attackers can deliberately exploit this to inflict financial damage. Even without malicious intent, poorly implemented integrations or buggy client code can accidentally cause runaway costs without rate limiting protection.
Prevention Checklist
Apply rate limits to every public-facing endpoint, not just authentication. Use algorithms like token bucket, leaky bucket, or fixed/sliding windows to control request rates. Different endpoints should have different limits based on their resource cost and sensitivity. Authentication endpoints need stricter limits (e.g., 5 requests/minute) while read-only data endpoints might allow higher rates (e.g., 100 requests/minute).
Implement rate limiting at multiple levels: per IP address, per authenticated user, per API key, and globally per endpoint. This provides defense in depth—attackers using multiple IPs can still be limited by user-level throttling, and distributed attacks can be caught by global endpoint limits. Consider different time windows (per second, per minute, per hour, per day) for comprehensive protection.
When rate limits are exceeded, return HTTP 429 (Too Many Requests) status codes with Retry-After headers indicating when the client can retry. Include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers in all responses so clients can self-regulate. Clear error messages help legitimate users understand the restriction while preventing attackers from bypassing detection.
In distributed systems, use centralized rate limiting stores like Redis or Memcached to share rate limit counters across multiple server instances. Libraries like Bucket4j (Java), node-rate-limiter-flexible (Node.js), or Flask-Limiter (Python) support distributed backends. API gateways like Kong, Tyk, or AWS API Gateway provide built-in distributed rate limiting capabilities.
Log all rate limit violations with client identifiers (IP, user ID, API key) and endpoint details. Set up alerts for unusual patterns like the same IP hitting limits repeatedly, or sudden spikes in rate limit violations across the system. These patterns often indicate active attacks. Use monitoring tools to track rate limit metrics and adjust thresholds based on legitimate usage patterns.
Consider more sophisticated strategies like adaptive rate limiting that tightens limits when attack patterns are detected, or progressive delays that increase response times for repeat violators. Implement CAPTCHA challenges for clients that repeatedly hit rate limits on authentication endpoints. For paid API tiers, enforce usage quotas and throttle gracefully when approaching limits rather than abruptly blocking requests.
Real-World Examples
GitHub API Abuse
Researchers discovered that GitHub's API could be abused to enumerate private email addresses of users. Without sufficient rate limiting on certain API endpoints, attackers were able to scrape millions of email addresses by systematically querying user profiles and commit histories. This exposed users to potential phishing campaigns and spam, leading GitHub to implement stricter rate limiting and remove email exposure from public APIs.
Venmo Public Transaction Scraping
Privacy researchers exploited Venmo's lack of rate limiting to scrape over 200 million public transactions, creating a searchable database that exposed users' payment histories, social connections, and behavioral patterns. The scraping was possible because Venmo's public API endpoint had insufficient rate limiting controls, allowing automated collection of vast amounts of transaction data that revealed sensitive personal information.
Twitter API Rate Limit Bypass
Security researchers identified methods to bypass Twitter's rate limiting by exploiting inconsistencies between different API endpoints and authentication methods. Attackers could scrape significantly more data than intended by rotating between multiple API keys, using different endpoint combinations, and exploiting legacy API versions with weaker rate limiting. This led to unauthorized mass data collection and privacy violations.
Clubhouse Data Breach
The audio social network Clubhouse suffered a data breach when attackers exploited weak rate limiting on user profile APIs. An attacker scraped 1.3 million user records including profile data, photos, usernames, and social connections by making automated requests to enumerate users. The lack of adequate rate limiting allowed the attacker to extract the entire user database and publish it publicly, compromising user privacy.
Ready to Test Your Knowledge?
Put what you have learned into practice. Try identifying and implementing rate limiting in our interactive coding challenges, or explore more security guides to deepen your understanding.