Input and resource controls
Protect sensitive business flows
Defend high-value actions such as purchasing, invitations, reservations, and password reset from automation and logic abuse.
A request can pass every technical check and still abuse your product. Someone writes a script that buys all your limited stock in two seconds. Each purchase is valid. The damage is in the total.
This is a business-flow attack. The schema is fine, the auth is fine, the rate limit on the endpoint may even be fine. What’s broken is what the flow allows in aggregate.
Find the flows worth attacking
Look for actions that are scarce, valuable, or hard to undo. Buying limited inventory. Sending invitations. Reserving slots. Triggering password reset emails. Claiming referral rewards.
For each one, ask: what happens when a scripted client runs this a thousand times an hour?
Take invitations. The endpoint can pass every schema check while sending 10,000 emails. The attacker pays nothing. You pay the provider bill, and your whole domain loses sending reputation.
Layer limits on the flow, not just the endpoint
A single per-endpoint rate limit is not enough here. Add limits per identity and per context, plus idempotency, confirmation steps, monitoring, and manual review where the impact justifies it.
For invitations, I would start with three limits:
invitations:
per-account: 50 / day
per-destination: 3 / week # the same inbox cannot be flooded
per-signup-ip: 5 / hour # fresh accounts do not reset limits
The per-destination limit matters more than it looks. An attacker can create ten accounts and defeat a per-account cap. But the target inbox stays the same across all ten, so the per-destination limit still holds.
A CAPTCHA alone won’t save you. It slows bots at signup, but it does nothing to cap cost once a valid session is automated. Avoid defenses that only slow down honest users.
Then check that the cap holds:
for i in $(seq 1 60); do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST https://api.example.com/invitations \
-H "Authorization: Bearer $TOKEN" \
-d '{"email":"person'$i'@example.com"}'
done
# 201 for the first 50, then 429 — and exactly 50 emails delivered
Notice the last part. Count the actual deliveries in your email provider’s log. A limiter that returns 429 after the send already went out protects nothing.
Leave a recovery path
Limits catch attackers, and they also catch real customers. A company launching a company-wide rollout will hit the invitation cap on day one, for good reasons.
So decide in advance: who can approve a larger campaign, how long the exception lasts, and which signal tells you the “legitimate spike” is abuse after all. Write those three answers next to the limits.
Try this on one flow in your product: set per-account and per-destination limits for invitations and write down the expected email count. Replay requests across several sessions. Prove delivery stops at the cap, an event gets logged, and a real customer still has a way to get their limit raised.
Lesson completed