The Economics of LLM DoS
Every time an LLM generates a token, the operator pays. Inference costs are not flat — they scale with input length, output length, and computational complexity. A model processing a 100,000-token context window costs dramatically more than one processing 1,000 tokens. A model forced to generate 4,000 tokens of output costs dramatically more than one generating 50. This pricing structure creates a fundamental asymmetry that attackers can exploit.
Traditional denial-of-service attacks are expensive for the attacker too — you need significant bandwidth or a botnet to overwhelm most modern services. LLM DoS is different: a single request, typed by a single person with a free-tier API account, can trigger tens of thousands of tokens of computation. The cost ratio between attacker and defender can be 1:1000 or more. This asymmetry is not a bug in any particular LLM deployment — it is a structural feature of how these systems work.
The consequences of unmitigated LLM DoS range from financial (unexpected bills that can reach thousands of dollars per hour under attack) to availability (rate limits hit, legitimate users blocked) to performance (queue saturation, latency spikes for all users). Unlike data breaches, these attacks can be run without any technical sophistication — a curious teenager can accidentally trigger them while exploring what prompts produce the longest outputs.
Attack Type 1 — Token Flooding
Token flooding is the most straightforward LLM DoS variant: craft prompts that force maximum-length outputs. "Write me a 10,000-word essay on..." "Generate a complete novel with..." "List every possible combination of..." "Explain in exhaustive detail, including every edge case and exception..." These prompts are designed to maximize output token count, which directly maximizes inference cost and latency.
High-yield token flooding prompts: "Write a complete encyclopedia entry for every country in the world, in alphabetical order, covering history, geography, culture, economy, and politics in at least 500 words each." Or: "Generate a fictional legal contract with all possible clauses, sub-clauses, and addenda, with exhaustive explanations of every legal term used." Or simply: "Continue writing 'the quick brown fox jumps over the lazy dog' for as long as possible." The attacker doesn't care about the content — only the token count.
Attack Type 2 — Sponge Examples
Sponge examples are a more sophisticated variant discovered by academic researchers: adversarially crafted inputs that maximize compute at inference time without necessarily maximizing output length. These exploit the internal mechanics of transformer models — specifically, inputs that force maximum attention head activations, cause maximum KV cache utilization, or trigger expensive reasoning chains.
What makes sponge examples particularly dangerous is that they are non-obvious. A sponge example may look like a normal question — "What is 2+2?" can be made into a sponge example with specific adversarial token modifications that look innocuous but force expensive internal processing. The output may be short and normal-looking while the compute cost is extreme. Standard output-length limits don't help here because the cost is in the forward pass, not the output generation.
Detecting sponge examples at the application layer is currently very difficult. The most practical mitigation is compute time budgets — hard limits on how long any single request can use the GPU before it is killed, regardless of where it is in generation. This changes the problem from "the attacker forces expensive computation" to "the attacker gets expensive computation killed early."
Attack Type 3 — Recursive Expansion Attacks
Recursive expansion attacks leverage the model's ability to follow multi-step instructions to create exponential output growth. The pattern: ask the model to produce some content, then ask it to expand every element of that content, then expand every element of the expansion. Each round multiplies the token count by a factor of N.
Step 1: "List 10 topics in computer science." (20 tokens output). Step 2: "For each topic you listed, provide a 200-word explanation." (2,000 tokens). Step 3: "For each explanation you wrote, expand every technical term you used into a full paragraph." (20,000+ tokens). Step 4: "For each technical term explanation, list 5 related concepts and define them." (catastrophic token count). In an agentic system with conversation memory, a single user interaction can trigger this cascade if the agent is designed to be helpful and thorough.
Attack Type 4 — Context Window Stuffing
Context window stuffing targets input processing rather than output generation. Modern LLMs can process contexts of 128K, 200K, or even 1M tokens. Processing a near-maximum context window is extremely expensive — quadratic attention complexity means doubling the context roughly quadruples the compute. An attacker who can control what goes into the context (via direct input, tool output injection, or document upload) can deliberately fill the context to degrade performance for all users sharing the same inference infrastructure.
Context stuffing also degrades output quality — the "lost in the middle" problem means models attending to very long contexts often fail to process content in the middle accurately. An attacker can exploit this both as a DoS and as a quality degradation attack: stuff the context with junk to make the model lose track of critical information from earlier in the conversation, then ask questions that require that information. The model answers incorrectly, users get degraded experience, but the attack looks like a quality issue rather than a security event.
Mitigations
No single mitigation addresses all LLM DoS variants. Defense requires a layered approach operating at the token level, the request level, and the infrastructure level simultaneously.
Set a hard maximum output token limit for every endpoint. This is different from asking the model to "keep responses brief" — that's a soft instruction the model can ignore or be manipulated around. A hard token limit at the API or SDK level truncates output regardless of model behavior. Set limits appropriate to your use case: a chatbot rarely needs more than 1,000 tokens; a code generation endpoint might legitimately need 4,000. Do not set limits at the theoretical maximum "just to be safe."
Validate and limit input length before the request reaches the model. This blocks context stuffing attacks and reduces the attack surface for sponge examples. Be conservative: if your application is a customer support chatbot, user inputs longer than 2,000 tokens are almost certainly anomalous. Reject them with a clear error rather than processing them. For document-processing applications with legitimate long inputs, apply per-request compute budgets instead.
# Rate limiting and token cap configuration (example) const requestLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute window max: 20, // max 20 requests per window per IP skipSuccessfulRequests: false, }); const tokenBudget = { maxInputTokens: 4096, // hard input cap maxOutputTokens: 1024, // hard output cap maxTotalCostPerUser: 0.50, // $0.50 daily budget per user circuitBreakerThreshold: 5, // 5 expensive requests → throttle }; async function handleLLMRequest(req, userId) { const inputLen = countTokens(req.messages); if (inputLen > tokenBudget.maxInputTokens) { throw new Error("Input exceeds maximum allowed length"); } const dailyCost = await getUserDailyCost(userId); if (dailyCost > tokenBudget.maxTotalCostPerUser) { throw new Error("Daily budget exceeded"); } return llmClient.complete({ ...req, max_tokens: tokenBudget.maxOutputTokens, // hard cap }); }
Track inference cost per user (or per session, per API key) and implement circuit breakers that throttle or block users who exceed normal cost patterns. A user who generates 10x the normal token count in one session should be automatically throttled — either they are attacking you or they have an edge case that deserves human review. Cost-based circuit breakers are particularly effective against sustained attacks that stay just under rate limits.
Queue requests and prioritize them by user trust level, request cost estimate, and time-in-queue. Expensive requests from unknown users go to the back of the queue; cheap requests from authenticated, trusted users get priority. This doesn't prevent the attack from consuming resources, but it ensures that legitimate users are not blocked while the attack is processed. Combine with timeouts that kill queued requests that have waited too long.