Rate limiting is applied per tenant
Why it matters
Global rate limits in multi-tenant systems create a noisy-neighbor availability problem: a single tenant with a burst of traffic — legitimate or scripted — exhausts the shared token bucket and causes 429 responses for all other tenants. CWE-770 (Allocation of Resources Without Limits or Throttling) is the relevant defect. While this is a low-severity finding in isolation, it becomes a practical denial-of-service vector in SaaS products with free tiers where abuse is common and the impact falls on paying customers.
Severity rationale
Low because the impact is availability degradation for other tenants rather than data exposure, and is bounded by the rate limit window duration.
Remediation
Partition rate limit buckets by tenant identifier so one tenant's burst cannot exhaust another's capacity. In src/middleware.ts, include the organization ID in the rate limit key:
// Upstash Ratelimit — per-tenant sliding window
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(100, '1 m'),
prefix: `ratelimit:org:${session.user.organizationId}`
})
const { success } = await ratelimit.limit(session.user.organizationId)
if (!success) return new Response('Rate limit exceeded', { status: 429 })
For plans with different quotas (free vs. paid), vary the slidingWindow limit based on the tenant's subscription tier retrieved from session claims, not from a per-request DB lookup.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Multi-Tenancy Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-770 — Allocation of Resources Without Limits or Throttling
- iso-25010:2011 · performance-efficiency — Performance Efficiency
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-multi-tenancy·automated