How We Stopped a 340-Bot Registration Attack in 4 Hours
April 21, 2026 · 8 min read
TL;DR: A Russian bot farm created 340 fake accounts on AIPower overnight, burning ~$14 of upstream API costs. We didn't just block them — we restructured the economics so attacking our service became unprofitable. Here's the full post-mortem.
The Attack
We woke up to our admin dashboard showing 366 total users. The day before: 13. The math didn't add up — we hadn't launched a marketing campaign. Three red flags surfaced:
- Email pattern: 188 Gmail + 150 Hotmail, all with Russian-transliterated names (
kistinaalekseya,polinashmatkova360) or random strings (mo6xxybjva6z,0uvxqti4m6). - Disposable domains: A cluster of freshly-registered ones —
rqmapi.asia,animateany.com,deepask.app,globalsmv.com. All had valid MX but zero organic traffic. - Usage pattern: Most accounts with 0 calls (just hoarding free credits for later), a few burning through 50+ calls on our most expensive models.
Total damage before we noticed: $14.64 in upstream API costs paid to OpenAI / Anthropic / DeepSeek. Small in absolute terms. But the per-account ROI for the attacker was real — free credit × bot count = meaningful cost to us.
Root Cause: Free Tier Economics
Our free tier was 50 API calls at signup, no credit card — industry standard. For the attacker:
cost_per_bot = $0.01 (email + CAPTCHA solve)
value_per_bot = 50 calls × $0.03/call = $1.50
ROI = +$1.49 per bot
× 340 bots = +$500 theoretical yieldThe actual yield was lower because most bots hoarded rather than burned — but the math was favorable to the attacker. That was the real vulnerability. Not a code bug. An economic bug.
The 7-Layer Defense We Built
Over the next 4 hours, we stacked layers. Each layer increases attacker cost slightly; together they flip the ROI negative.
L-1 — User-Agent filter
Reject curl, python-requests, go-http-client, node-fetch, httpie, axios on /api/auth/register. Real humans use browsers. This instantly blocks scripted bots (the majority of volume), forcing attackers to invest in headless browsers. Cost: +$0.005/bot.
L0 — Cloudflare Turnstile
Free, invisible CAPTCHA. Blocks ~95% of headless browser bots. The remaining 5% need a captcha-solving service like 2Captcha (~$3/1000 solves). Cost: +$0.003/bot.
L1 — Email validation
Four sub-checks in order: (a) disposable domain blacklist (48 domains including all we'd observed), (b) suspicious TLD list (.asia, .cfd, .site, etc.), (c) keyboard-mash detection for local parts (no-vowel runs, random-digit scatter), (d) MX record verification. Together these block > 80% of low-effort bots without catching real emails.
A key pattern we caught: mo6XXXXXXX@domain.com — 12 bots all shared the mo6 prefix across different domains. Cost to the attacker to rewrite: real dev time.
L2 — IP and per-domain rate limits
Max 5 registrations per IP per hour, max 10 per mail provider (Gmail, Hotmail, etc.) per 10 minutes. Forces attackers to rotate IPs and email domains. Residential proxy pools aren't free ($1-5/GB).
L3 — Mandatory email verification
Resend-sent 6-digit code, 10-minute TTL. Bots need working inboxes — so they buy pre-aged Gmail accounts ($0.5-2 each). This is where attacker cost first exceeds value.
L4 — Lower free tier + model restriction
Cut signup credits from 50 → 2 (not a typo — two). Expensive models (Claude Opus, GPT-5.4, Gemini Pro) are off-limits until the user binds a card. This drops attacker value from $1.50/bot to ~$0.001/bot.
L5 — Free-tier daily call cap
Even after all defenses, any unpaid account is capped at 5 calls/day. Stretches the attacker's daily yield to near zero.
L6 — Hourly registration-rate alert
A KV counter per hour; email alert to ops at thresholds 10 / 50 / 200 per hour. Idempotent per hour per threshold. Means the next wave will wake us up within 60 minutes.
New ROI Math
cost_per_bot =
$1 (pre-aged Gmail account)
+ $0.003 (CAPTCHA solve)
+ $0.005 (residential IP rotation)
≈ $1.008
value_per_bot =
2 calls × $0.0005 (cheap models only)
= $0.001
ROI = -$1.007 per botWe didn't stop bots. We made bots bad business. The next wave arrived 6 hours after Turnstile went live: zero successful registrations.
Lessons
- Don't try to block everything. Impossible. Make the attack unprofitable.
- Layer defenses. Any single layer fails. Seven layers multiply attacker cost.
- Watch the economics. A $50 free tier isn't generous — it's a $50 bounty. Consider giving less at signup, more at card-bind, most at first payment.
- Instrument everything. Without the rate alert, the next attack will also take 12+ hours to notice.
- Audit the ripple. The bot accounts also triggered a negative balance bug in our billing code that was invisible under normal load. Attacks surface latent bugs.
Tools We Used
- Cloudflare Turnstile (free) — invisible CAPTCHA
- Cloudflare Workers + D1 (edge + SQL) — where all this runs
- Resend — branded verification emails
- Stripe SetupIntent ($0 hold) — card binding without a charge
- KV rate counters — cheap, expire automatically
Try AIPower
AIPower is the AI API gateway we built this defense for — one endpoint, 16 models (GPT-5.4, Claude, DeepSeek, Qwen, Gemini), OpenAI SDK compatible. Small team, fast to respond. Get your free API key at aipower.me — 2 free calls, +100 on first top-up.