Skip to main content

Overview

This vulnerability occurs when an application fails to limit the number or frequency of requests a user (or IP address) can make to sensitive endpoints within a given time window. Without rate limiting, attackers can perform automated attacks like:
  • Brute-forcing credentials: Rapidly guessing passwords on a login page.
  • Brute-forcing tokens: Guessing password reset tokens, 2FA codes, or session IDs.
  • Denial of Service (DoS): Overwhelming resource-intensive endpoints (like search or complex calculations) with excessive requests.
  • API Abuse: Exceeding fair usage quotas for API endpoints.
  • Web Scraping: Extracting large amounts of data rapidly. ⏱️💥

Business Impact

Missing rate limiting can lead to:
  • Account Takeover: Successful brute-force attacks compromise user accounts.
  • Denial of Service: Legitimate users may be locked out or experience slow performance due to resource exhaustion caused by automated attacks.
  • Increased Costs: Excessive use of resource-intensive functions or paid API calls can inflate operational costs.
  • Data Scraping: Competitors or attackers can easily scrape large volumes of public or semi-public data.

Reference Details

CWE ID: CWE-799 OWASP Top 10 (2021): A04:2021 - Insecure Design Severity: Medium to High

Framework-Specific Analysis and Remediation

Rate limiting is typically implemented using middleware or dedicated libraries that track request counts per user ID, IP address, or API key over time (e.g., X requests per minute). Frameworks often have plugins or built-in support for this. Key Remediation Principles:
  1. Identify Sensitive Endpoints: Apply rate limiting primarily to login attempts, password reset requests, token validations, expensive API calls, and form submissions.
  2. Choose Appropriate Limits: Set reasonable limits based on expected legitimate usage (e.g., 5-10 login attempts per minute per IP/user).
  3. Track by IP and/or User ID: Track anonymous requests (like login attempts) by IP address. Track authenticated requests by user ID and potentially IP address for better protection.
  4. Implement Exponential Backoff (Optional but Recommended): Increase the delay for subsequent attempts after a limit is reached.
  5. Logging and Monitoring: Log rate limit events to detect attacks.

  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Using libraries like django-ratelimit, Flask-Limiter, or implementing custom logic using caching backends (Redis, Memcached).

Vulnerable Scenario 1: No Login Attempt Limit (Django)

# accounts/views.py
# Standard Django LoginView without rate limiting applied

class UserLoginView(LoginView):
    template_name = 'accounts/login.html'
    # DANGEROUS: No mechanism to prevent an attacker from submitting
    # thousands of password guesses per minute for a given username.

Vulnerable Scenario 2: Unprotected API Endpoint (Flask)

# app.py (Flask)
@app.route('/api/search', methods=['GET'])
def api_search():
    query = request.args.get('q')
    # DANGEROUS: This search might be resource-intensive.
    # An attacker can flood this endpoint, causing DoS.
    # No rate limiting is applied.
    results = perform_complex_search(query)
    return jsonify(results)

Mitigation and Best Practices

Integrate a rate-limiting library. Apply decorators or middleware to the specific views/routes that need protection. Use Redis or Memcached for distributed rate limiting in multi-server environments.

Secure Code Example

# views.py (Django with django-ratelimit)
from ratelimit.decorators import ratelimit

class UserLoginViewSecure(LoginView):
    template_name = 'accounts/login.html'

    # SECURE: Apply rate limiting decorator.
    # block=True returns 429 Too Many Requests when limit is exceeded.
    # key='ip' tracks by IP. key='user_or_ip' tracks by user if logged in, else IP.
    # key='post:username' tracks based on the 'username' field in the POST data + IP.
    @method_decorator(ratelimit(key='post:username', rate='5/m', block=True), name='dispatch')
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

# app.py (Flask with Flask-Limiter)
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    get_remote_address, # Use IP address for tracking
    app=app,
    default_limits=["200 per day", "50 per hour"],
    storage_uri="memory://" # Use "redis://localhost:6379" etc. for production
)

@app.route('/api/search-secure')
@limiter.limit("10 per minute") # SECURE: Specific limit for this endpoint
def api_search_secure():
    query = request.args.get('q')
    results = perform_complex_search(query)
    return jsonify(results)

Testing Strategy

Use automated tools (like wfuzz, ffuf, Burp Intruder) or simple scripts (e.g., curl in a loop) to send rapid, repeated requests to login endpoints, password reset forms, and resource-intensive API endpoints. Verify that after exceeding the configured limit, the server responds with an appropriate error (e.g., 429 Too Many Requests) and blocks further requests for a period. Test different keys (IP vs. user vs. form field).