Missing Rate Limiting Anti-Pattern
Severity: High
Summary
Applications fail to restrict action frequency, allowing unlimited requests to endpoints. Enables brute-force attacks, data scraping, and denial-of-service through resource-intensive requests.
The Anti-Pattern
The anti-pattern is exposing endpoints (especially authentication/resource-intensive) without controlling request frequency per user or IP.
BAD Code Example
# VULNERABLE: The login endpoint has no rate limiting.
from flask import request, jsonify
@app.route("/api/login", methods=["POST"])
def login():
username = request.form.get("username")
password = request.form.get("password")
# Endpoint callable thousands of times per minute from same IP.
# Attacker uses password lists for brute-force/credential stuffing,
# trying millions of passwords until finding correct one.
if check_credentials(username, password):
return jsonify({"status": "success", "token": generate_token(username)})
else:
return jsonify({"status": "failed"}), 401
# Search endpoint without rate limiting.
@app.route("/api/search")
def search():
query = request.args.get("q")
# Attacker rapidly hits endpoint, scraping data or causing
# DoS through heavy database work.
results = perform_complex_search(query)
return jsonify(results)
GOOD Code Example
# SECURE: Implement rate limiting using middleware and a tracking backend like Redis.
from flask import request, jsonify
from redis import Redis
from functools import wraps
redis = Redis()
def rate_limit(limit, per, scope_func):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
key = f"rate-limit:{scope_func(request)}:{request.endpoint}"
# Increment count for current key.
# Expire after `per` seconds on first request in window.
p = redis.pipeline()
p.incr(key)
p.expire(key, per)
count = p.execute()[0]
if count > limit:
return jsonify({"error": "Rate limit exceeded"}), 429
return f(*args, **kwargs)
return decorated_function
return decorator
# Get identifier for rate limit scope (IP address).
def get_ip(request):
return request.remote_addr
# Apply different rate limits per endpoint.
@app.route("/api/login", methods=["POST"])
@rate_limit(limit=10, per=60*5, scope_func=get_ip) # 10 requests/5min per IP
def login_secure():
# ... login logic ...
pass
@app.route("/api/search")
@rate_limit(limit=100, per=60, scope_func=get_ip) # 100 requests/min per IP
def search_secure():
# ... search logic ...
pass
Detection
- Review public endpoints: Examine all endpoints that can be accessed without authentication. Do they have rate limiting?
- Check authentication endpoints: Specifically look at login, password reset, and registration endpoints. These are prime targets for brute-force attacks if not rate-limited.
- Analyze API design: For public APIs, check if there is a documented rate-limiting policy (e.g., in the API documentation).
- Perform testing: Write a simple script to hit a single endpoint in a tight loop. If you don't receive a
429 Too Many Requests status code after a certain number of attempts, the endpoint is likely missing rate limiting.
Prevention
Related Security Patterns & Anti-Patterns
References
1---2name: missing-rate-limiting-anti-pattern3description: Security anti-pattern for missing rate limiting (CWE-770). Use when generating or reviewing API endpoints, authentication systems, or public-facing services. Detects absence of request throttling enabling brute force, credential stuffing, and DoS attacks.4---56# Missing Rate Limiting Anti-Pattern78**Severity:** High910## Summary1112Applications fail to restrict action frequency, allowing unlimited requests to endpoints. Enables brute-force attacks, data scraping, and denial-of-service through resource-intensive requests.1314## The Anti-Pattern1516The anti-pattern is exposing endpoints (especially authentication/resource-intensive) without controlling request frequency per user or IP.1718### BAD Code Example1920```python21# VULNERABLE: The login endpoint has no rate limiting.22from flask import request, jsonify2324@app.route("/api/login", methods=["POST"])25def login():26 username = request.form.get("username")27 password = request.form.get("password")2829 # Endpoint callable thousands of times per minute from same IP.30 # Attacker uses password lists for brute-force/credential stuffing,31 # trying millions of passwords until finding correct one.32 if check_credentials(username, password):33 return jsonify({"status": "success", "token": generate_token(username)})34 else:35 return jsonify({"status": "failed"}), 4013637# Search endpoint without rate limiting.38@app.route("/api/search")39def search():40 query = request.args.get("q")41 # Attacker rapidly hits endpoint, scraping data or causing42 # DoS through heavy database work.43 results = perform_complex_search(query)44 return jsonify(results)45```4647### GOOD Code Example4849```python50# SECURE: Implement rate limiting using middleware and a tracking backend like Redis.51from flask import request, jsonify52from redis import Redis53from functools import wraps5455redis = Redis()5657def rate_limit(limit, per, scope_func):58 def decorator(f):59 @wraps(f)60 def decorated_function(*args, **kwargs):61 key = f"rate-limit:{scope_func(request)}:{request.endpoint}"62 # Increment count for current key.63 # Expire after `per` seconds on first request in window.64 p = redis.pipeline()65 p.incr(key)66 p.expire(key, per)67 count = p.execute()[0]6869 if count > limit:70 return jsonify({"error": "Rate limit exceeded"}), 4297172 return f(*args, **kwargs)73 return decorated_function74 return decorator7576# Get identifier for rate limit scope (IP address).77def get_ip(request):78 return request.remote_addr7980# Apply different rate limits per endpoint.81@app.route("/api/login", methods=["POST"])82@rate_limit(limit=10, per=60*5, scope_func=get_ip) # 10 requests/5min per IP83def login_secure():84 # ... login logic ...85 pass8687@app.route("/api/search")88@rate_limit(limit=100, per=60, scope_func=get_ip) # 100 requests/min per IP89def search_secure():90 # ... search logic ...91 pass92```9394## Detection9596- **Review public endpoints:** Examine all endpoints that can be accessed without authentication. Do they have rate limiting?97- **Check authentication endpoints:** Specifically look at login, password reset, and registration endpoints. These are prime targets for brute-force attacks if not rate-limited.98- **Analyze API design:** For public APIs, check if there is a documented rate-limiting policy (e.g., in the API documentation).99- **Perform testing:** Write a simple script to hit a single endpoint in a tight loop. If you don't receive a `429 Too Many Requests` status code after a certain number of attempts, the endpoint is likely missing rate limiting.100101## Prevention102103- [ ] **Implement IP-based rate limiting:** All public endpoints, especially authentication/sensitive ones.104- [ ] **Implement account-based rate limiting:** Prevent authenticated users from abusing system.105- [ ] **Use appropriate algorithm:** Token Bucket, Leaky Bucket, or Fixed/Sliding Window. Most frameworks have middleware.106- [ ] **Return `429 Too Many Requests`:** Include `Retry-After` header indicating retry time.107- [ ] **Log rate limit violations:** Identify and respond to potential attacks.108- [ ] **Consider account lockouts for login:** Additional defense after failed attempts.109110## Related Security Patterns & Anti-Patterns111112- [Missing Authentication Anti-Pattern](../missing-authentication/): Endpoints that are missing authentication are at even greater risk if they also lack rate limiting.113- [Denial of Service (DoS):](../#) Missing rate limiting is a primary cause of application-layer DoS vulnerabilities.114115## References116117- [OWASP Top 10 A06:2025 - Insecure Design](https://owasp.org/Top10/2025/A06_2025-Insecure_Design/)118- [OWASP GenAI LLM10:2025 - Unbounded Consumption](https://genai.owasp.org/llmrisk/llm10-unbounded-consumption/)119- [OWASP API Security API4:2023 - Unrestricted Resource Consumption](https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/)120- [OWASP Rate Limiting](https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html)121- [CWE-770: Resource Allocation Without Limits](https://cwe.mitre.org/data/definitions/770.html)122- [CAPEC-49: Password Brute Forcing](https://capec.mitre.org/data/definitions/49.html)123- [PortSwigger: Authentication](https://portswigger.net/web-security/authentication)124- Source: [sec-context](https://github.com/Arcanum-Sec/sec-context)