๐Ÿ›ก OWASP Lab
API4:2023 High

Unrestricted Resource Consumption

Theory

API4 Unrestricted Resource Consumption (formerly Lack of Resources & Rate Limiting) covers all cases where an API imposes no limits on the size or frequency of requests, allowing attackers to exhaust server resources, drive up cloud costs, or enumerate data at scale.

Why APIs Are More Exposed Than Web Apps

Traditional web applications are slowed by browser navigation (page loads, form submissions). API clients โ€” mobile apps, CLI tools, scripts โ€” can send thousands of requests per second. Without explicit limits, every endpoint is a potential denial-of-service target or mass data extraction tool.

Types of Unrestricted Resource Consumption

  • No pagination limit โ€” GET /users?limit=999999 returns the entire database in one request; DDoS + data exfiltration in one shot
  • No rate limiting โ€” the login endpoint, OTP endpoint, or password reset endpoint accepts unlimited requests per second; enables brute-force and credential stuffing
  • Unlimited batch operations โ€” POST /messages/send that accepts an array of recipients with no maximum count; one request can spam thousands of users
  • Unrestricted file uploads โ€” no file size limit on uploads; attacker fills the storage bucket or disk with gigabytes of data
  • Compute-heavy endpoints without throttling โ€” image processing, PDF generation, or ML inference endpoints called thousands of times per minute; cloud bill attack

Vulnerable Code

# VULNERABLE โ€” no maximum limit; no rate limiting
@app.get("/api/v1/users")
def list_users(limit: int = 10, user = Depends(get_current_user), db = Depends(get_db)):
    users = db.exec(select(User)).all()
    return users[:limit]   # limit is uncapped; GET /api/v1/users?limit=9999999 is accepted

# Attack: extract entire database with one request
curl -H 'Authorization: Bearer ...' '/api/v1/users?limit=9999999'

Fixed Code

from slowapi import Limiter

limiter = Limiter(key_func=get_remote_address)

MAX_PAGE_SIZE = 100

@app.get("/api/v1/users")
@limiter.limit("30/minute")   # max 30 requests per IP per minute
def list_users(request: Request, limit: int = 20, offset: int = 0,
               user = Depends(get_current_user), db = Depends(get_db)):
    capped_limit = min(limit, MAX_PAGE_SIZE)   # client can ask for less but not more
    users = db.exec(select(User).offset(offset).limit(capped_limit)).all()
    return {
        "data": users,
        "limit": capped_limit,
        "offset": offset,
        "total": db.exec(select(func.count(User.id))).one(),
    }

Resource Consumption in Cloud โ€” "Billion Laughs" for APIs

# An endpoint that fans out to multiple backend calls can be abused:
# POST /api/v1/bulk-send with {"recipients": [1, 2, 3, ..., 1000000]}
# Each recipient triggers an email, a DB write, and a push notification
# = 3 million backend operations from 1 HTTP request

# FIXED โ€” hard cap on batch size
class BulkSendRequest(BaseModel):
    recipients: list[int]

    @validator('recipients')
    def check_limit(cls, v):
        if len(v) > 100:
            raise ValueError('Maximum 100 recipients per request')
        return v

Real-World Incidents

  • Snapchat (2013) โ€” The Find Friends API had no rate limiting. A researcher dumped 4.6 million usernames and phone numbers in a single automated run.
  • USPS Informed Delivery (2018) โ€” No rate limit on the address search API; 60 million customer records enumerated by querying every USPS address in the country
  • AWS Lambda misconfiguration (ongoing) โ€” Public-facing Lambda functions with no throttling that trigger expensive downstream processes have led to accidental $50,000+ cloud bills in a single day

How to Fix โ€” Checklist

  • Cap all pagination parameters โ€” enforce a maximum (e.g. 100) regardless of what the client requests; return the capped value in the response
  • Rate limit every endpoint โ€” at minimum: login (5/min), SMS/email (3/min), expensive operations (10/min); use a middleware like slowapi or an API gateway
  • Hard limits on batch sizes โ€” every endpoint that accepts arrays should have a maximum array length validation at the schema level
  • Timeouts on all operations โ€” database queries, external HTTP calls, file uploads; never let a request run indefinitely
  • Monitor for spikes โ€” alert when a single IP makes >N requests in a time window; alert on P99 latency spikes (sign of resource exhaustion)
Challenge 1

No Pagination Limit โ€” Dump Entire User Database

The users endpoint has no enforced maximum for the limit parameter. Request all users at once. A hidden admin user with the flag appears when you fetch beyond the default page.

Hint
GET /api/v1/users?limit=9999