Rate Limiting & Abuse Prevention
Any endpoint open to the world will be hit hard — by brute-force login attempts, credential stuffing, scrapers, and accidental client loops. Rate limiting caps how often a caller can hit an endpoint, which turns these from a real threat into a blocked one. It is a cheap control that protects both security and availability.
Without limits, attackers can try millions of passwords, list out accounts, scrape data, or simply overwhelm the service. A buggy client can also take you down by accident. Rate limiting, and related controls (throttling, quotas, lockouts, and CAPTCHAs where they fit), limits the damage any single caller can do.
Apply it especially to sensitive and expensive endpoints: authentication, password reset, KYC, payments, and anything that triggers a costly downstream call. Combine it with the wider fail-safe approach (see Secure Defaults, Designing for Failure).
Limit and throttle
- AlwaysApply rate limits to authentication, password-reset, KYC, payment, and other sensitive or expensive endpoints by default (see Secure Defaults).
- DoKey limits by the right dimension — per IP, per account, per API key, per tenant — and often several together, so one abusive caller does not affect others.
- DoAdd progressive protections to auth. Slow down or temporarily lock after repeated failures, and watch for credential stuffing across many accounts.
- DoSet request-size and payload limits too, and apply quotas to partner and API access (see Partner API Access).
- ConsiderA CAPTCHA or step-up challenge for suspicious patterns on public endpoints (signup, reset), rather than blocking real users.
[HttpPost("/login")] public IActionResult Login(Creds c) { ... }
// no limit, no lockout
An attacker can try unlimited passwords against one account (brute force), or the same password across many accounts (credential stuffing), at machine speed. The door is wide open.
[EnableRateLimiting("auth")] // e.g. 5/min per IP+account, shared store
[HttpPost("/login")] public IActionResult Login(Creds c) { ... }
// repeated failures -> backoff/lock; spikes -> alert
Brute force and stuffing are slowed to a trickle that is no use, repeated failures trigger a lockout, and an attack shows up in monitoring.
Fail and respond well
- DoReturn a clear
429 Too Many Requestswith aRetry-Afterhint, so legitimate clients back off correctly. - DoMake limiting work across all instances (a shared/distributed limiter), not per-instance, where it is easy to bypass by load-balancing.
- DoFeed rate-limit and lockout events into monitoring. A spike is often an attack in progress (see Security Monitoring & Detection).
- DoFail safe if the limiter itself is unavailable. Prefer cautious limiting over removing the protection entirely.
- AvoidLimits so tight they break normal use, or error messages that reveal whether an account exists. Keep those messages generic.
Self-review checklist
- AskIs this sensitive or expensive endpoint rate-limited, and keyed by a dimension that actually stops abuse?
- AskCan someone brute-force or list out accounts here, or take the service down with a loop?
- AskDoes the limit work across all instances, not just one?
- AskDo limit and lockout events show up in monitoring?