09 — Rate Limiting & Abuse Prevention
Availability and abuse defense. Closes OWASP API4 (Unrestricted Resource Consumption) and API6 (Unrestricted Access to Sensitive Business Flows), and provides the availability half of tenant isolation (05).
1. Layered limits (each layer catches what the others miss)
flowchart TB
classDef l fill:#fde68a,stroke:#b45309,color:#111827;
req["request"]:::l --> e["① Edge/CDN/WAF: per-IP, DDoS, geo, bot"]:::l
e --> t["② Per-tenant: requests + quotas (noisy-neighbor isolation)"]:::l
t --> k["③ Per-user / per-API-key: scoped budgets"]:::l
k --> ep["④ Per-endpoint: auth & expensive ops stricter"]:::l
ep --> ok["allow / 429 + Retry-After"]:::l
| Layer | Limits | Stops |
|---|---|---|
| ① Edge | per-IP, WAF rules, DDoS scrubbing | volumetric floods, scanners |
| ② Per-tenant | request rate + quotas (storage, egress, function invocations) | noisy neighbor; tenant-level abuse |
| ③ Per-user / per-key | scoped budgets | a single compromised key/user |
| ④ Per-endpoint | login, share-password, search, transforms = stricter | brute force, expensive-op abuse |
Algorithm: token-bucket / sliding-window counters in Redis, updated atomically (Lua) for correctness under concurrency; distributed across gateway replicas.
2. Targeted abuse defenses
| Abuse | Defense |
|---|---|
| Login brute force / credential stuffing | exponential backoff + lockout + CAPTCHA + breach-password check + device/anomaly signals (03) |
| Share-password brute force | strict per-link rate limit + lockout |
| Enumeration (users, links, IDs) | uniform responses (no “user not found” oracle), rate limits, unguessable tokens (06) |
| Sensitive-flow abuse (mass link creation, bulk export, signup farms) | flow-level limits, step-up/CAPTCHA, anomaly detection (API6) |
| Expensive-op abuse (search, transforms, Functions) | dedicated stricter budgets + quotas |
| Upload floods | per-tenant ingest limits + staging GC (storage/05) |
3. The data-plane gap (be honest)
Presigned direct-to-storage transfers (ADR-0011) bypass our gateway rate limiting — the bytes go straight to the object store. We cannot rate-limit what we don’t see, so we compensate:
- Short-TTL, scoped, single-use-ish presigned URLs (small blast radius per URL).
- Object-store-side per-tenant egress limits / bucket policies + CDN rate limits where available.
- Quota enforcement at issuance time (refuse to presign over quota).
This is a deliberate, documented trade: we accept reduced in-band control over the data plane in exchange for the scalability of not proxying bytes (README §1.7).
4. Quotas (availability isolation)
Per-tenant quotas — storage bytes, transfer/egress, request counts, function invocations, share count — both bill usage (04 billing context) and contain a single tenant’s blast radius on shared resources (noisy-neighbor isolation, 05 §5).
5. DoS resilience
Edge/CDN/WAF absorb volumetric attacks; services autoscale (HPA/KEDA, platform/04); graceful degradation sheds non-critical load (e.g. pause transforms) before core read/write; fail-closed on auth (never fail-open to “allow” under load).
6. Threats addressed & residual
| Threat | Control | Residual |
|---|---|---|
| Resource exhaustion (API4) | layered limits + quotas | low |
| Sensitive-flow abuse (API6) | flow limits + step-up + anomaly | medium |
| Brute force / stuffing | backoff + lockout + CAPTCHA + MFA | low |
| Data-plane abuse | short-TTL URLs + object-store limits | medium (known gap) |
| Volumetric DoS | CDN/WAF + autoscale | low-med |
References
- OWASP API4 / API6 (2023): https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/
- OWASP Denial of Service Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html