Your support bot is someone's free ChatGPT
The last two days were about attackers who want to hijack your bot. This one has a simpler goal, and it’s more common: they just want free inference. If you’ve put a paid model behind a public endpoint without much in front of it — an open chat widget, an unauthenticated API route — you have, functionally, published a free ChatGPT on your credit card. And people find these.
How it gets drained
Your public feature calls a paid model. An abuser scripts your endpoint and pipes their own prompts through it — coding help, essay writing, their own product’s LLM calls — all billed to you. Anything the model can do, they now do on your dime. Off-topic use is just you funding strangers’ AI.
They find it the way they find open S3 buckets and exposed Elasticsearch: scrapers, shared “free GPT proxy” lists, and anyone who opens browser devtools on your site and sees the network call to your chat route. An open LLM endpoint gets discovered fast — often within days of going live.
It’s not only a cost problem
This is LLM10 Unbounded Consumption with a human driving, but there’s a second bill: your provider account. Abusers generate content that violates the model provider’s usage policy — through your key. It’s your account that gets rate-limited, flagged, or banned. So a freeloading attack can take your real feature down, not just run up a number.
The defenses (auth + quotas — the base, new target)
- Auth first. Don’t expose a paid model to anonymous traffic if you can avoid it. Put it behind a login, or at least a session token you issue and can revoke. (This is Day 13’s whole topic — auth on AI endpoints.)
- Per-user / per-key quotas + rate limits. Straight from Day 4: even legitimate users get a budget and a requests-per-minute ceiling, so an abusive or compromised account is boxed to its own limit instead of draining everything.
- A hard global cap. The fail-closed backstop. When abuse spikes past everything else, the cap trips before the invoice does.
- Topic-locking and scope constraints. A support bot should refuse to write someone’s essay. If it’s being asked to produce code or content unrelated to your product, that’s a signal — flag or refuse.
- Origin checks, CAPTCHA, or proof-of-work on public widgets — raise the cost of scripted abuse so freeloading isn’t frictionless.
- Abuse detection at the gateway. High volume, off-topic patterns, classic “write me X” prompts — the gateway sees them all in one place.
The gate in code
The three deterministic controls — identify, meter, scope — are one small wrapper in front of the model call, not a rewrite of the feature:
from fastapi import FastAPI, Header, HTTPException, Depends
from datetime import date
app = FastAPI()
def caller(x_session: str = Header(...)) -> str:
user = SESSIONS.get(x_session) # (a) identify — no valid token,
if not user: # no model. Anonymous traffic stops here.
raise HTTPException(401, "auth required")
return user
@app.post("/support/ask")
def ask(question: str, user: str = Depends(caller)):
key = f"quota:{user}:{date.today()}"
used = redis.incr(key); redis.expire(key, 86400) # (b) per-identity daily counter
if used > DAILY_LIMIT: # one account can't drain the pool
raise HTTPException(429, "daily limit reached")
return llm.chat(messages=[ # (c) scope to the task — SOFT, a cost-reducer
{"role": "system", "content":
"You are ACME's product-support bot. Answer only about ACME; refuse other requests."},
{"role": "user", "content": question},
]) # your existing provider call, unchanged
The caller dependency turns away anonymous traffic before a single token is billed (a), and the Redis
counter boxes each identity to its own daily budget so one scripted account can’t drain everything (b).
Note the order: (a) and (b) are the real boundaries and they run first — the scoped system prompt (c)
is soft, a cost-reducer an injection can argue with, so the quota sits in front of it, never behind it.
The honest part
Topic-locking via the prompt is soft. It’s an instruction, and — per Day 7 — instructions can be out-argued by an injection that “unlocks” the assistant. So treat topic-locking as a cost-reducer, not a boundary. The real boundaries here are the deterministic ones: auth, per-user quotas, and a hard cap. Those don’t care how clever the prompt is.
It's not just your bill — abuse can get your provider account banned.
And a legitimate exception: sometimes an open, unauthenticated demo is the point — a marketing trial, a public playground. That’s a fine choice. But then quotas, rate limits, a global cap, and abuse detection aren’t optional, and you’re deciding, deliberately, to accept some abuse as a capped marketing cost — with a ceiling you set, not one the attacker sets for you.
Next in the series — Day 10: when your bot promises a $1 truck — the risk that isn’t the bill at all, but the thing your bot says that you’re suddenly on the hook for.
If your AI feature is reachable without a login, assume someone’s already testing whether it’s free — and closing that is part of the audit I do. Every message comes straight to me — I read and reply to each one myself, usually within a day, and what readers send shapes what I build next. It’s just me for now, so that’s genuinely true; it won’t be forever. Send me your endpoint setup and I’ll tell you whether you’re running a free ChatGPT — free, within a business day.