Engineering

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 yield

The 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 bot

We didn't stop bots. We made bots bad business. The next wave arrived 6 hours after Turnstile went live: zero successful registrations.

Lessons

  1. Don't try to block everything. Impossible. Make the attack unprofitable.
  2. Layer defenses. Any single layer fails. Seven layers multiply attacker cost.
  3. 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.
  4. Instrument everything. Without the rate alert, the next attack will also take 12+ hours to notice.
  5. 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.

Ready to try?

2 free API calls. 16 models. One API key.

Create free account