Chapter 04 of 12
Unrestricted Resource Consumption
API4:2023 is a design failure rather than an operations problem, covering unbounded pages, oversized bodies, missing limits and the operations that cost money per call.
This category reads like an operations problem and it is a design problem. Nearly every instance is a bound that nobody decided, and a bound is part of the contract.
OWASP lists it as API4:2023 Unrestricted Resource Consumption, with references to CWE-770, CWE-400 and CWE-799.
What gets consumed
The obvious resource is compute, and it is rarely the expensive one.
Memory goes first, usually to a response that was materialised in full before being serialised. Connections and worker threads follow, because a slow endpoint under load holds them. Storage grows quietly. The one that produces the largest number is third-party cost, since every request that fans out to a paid service, a model provider or an SMS gateway converts traffic straight into an invoice.
That last case is why this category is worth taking seriously even on an API with no availability risk. Nothing goes down, and the month ends with a bill nobody can explain.
The four bounds worth writing down
Page size. A list endpoint with no cap will eventually be asked for everything, and the caller doing it is usually a client with a retry loop rather than an attacker. Document a default and a maximum, then clamp a larger request rather than serving it. The pagination chapter covers the shapes.
Body size. Enforce the limit before parsing, because a parser that has already allocated is a parser that has already lost. The correct refusal is 413 Content Too Large, and the common failure is a 500, which tells an attacker that the payload reached something.
Request rate. Per caller rather than per address, since addresses are shared by offices and rented by attackers. Expensive operations deserve a tighter budget than cheap ones, which is a per-endpoint decision. What a caller is told when they hit the limit is covered in rate limits as a design decision.
Depth and fan-out. Any parameter that multiplies work needs its own ceiling, including nested expansion, batch sizes, date ranges over a reporting endpoint and the number of items in a bulk write. Each of these is a small integer in a request body that turns into a large number of database round trips.
Testing this without becoming the incident
Two of these bounds can be checked with a single well-formed request, and one of them cannot.
Asking a collection endpoint for an absurd page size, and sending a write endpoint a body larger than the documented maximum, are both single requests. They tell you whether the bound exists, they generate almost no load, and they are safe to run on every build.
Checking a rate limit is different in kind, because the only way to observe a limit is to exceed it. That produces a burst against a live service, so it belongs on an environment you own, at a time you chose, rather than in the pull request pass. Treat it as opt-in and keep it opt-in.
In Routebase
api4-resource-consumption runs the two safe probes by default and keeps the third behind a switch.
The unbounded pagination probe sends collection endpoints an implausible page size and reports a High finding when the response is a 2xx with an uncapped page rather than a refusal or a clamp. The oversized payload probe sends write endpoints a large body, sized by the profile's Max probe payload setting, which defaults to about one megabyte and is configurable from one kilobyte up to ten megabytes. It flags both a 2xx, meaning the body was accepted, and a 5xx, meaning it crashed something, because the healthy answer is a 413.
The rate-limit burst is the opt-in. The profile carries an Enable rate-limit probe toggle that is off by default, and turning it on fires a short burst of requests at the first collection endpoint to see whether a 429 ever appears. It is off by default because a burst can stress a shared target, so turn it on only against an environment you own.
The two fuzzing scanners belong to this category as well, and they sit behind a second gate for the same reason. fuzz-schema generates payloads that violate the declared types, lengths and formats, while fuzz-mutation starts from a request that provably worked and corrupts parts of it. Both send no requests at all while the profile's Fuzzing intensity is Off, even when they are selected, and raising the intensity enables more payload classes and a larger request budget.
On the design side, the style guide rule should-have-rate-limit-headers checks whether a success response declares rate-limit headers at all, which is the contract half of the same question. See Scan Profiles for the limits and the two gates, and the Scanner Reference for what each probe sends.
Frequently asked questions
What is unrestricted resource consumption?
It is an API that lets one caller consume memory, connections, storage, third-party cost or quota without a bound. OWASP lists it as API4:2023, and its references name CWE-770, Allocation of Resources Without Limits or Throttling, CWE-400, Uncontrolled Resource Consumption, and CWE-799, Improper Control of Interaction Frequency. The damage is often a bill rather than an outage.
Should a list endpoint cap its page size?
Yes, and the cap belongs in the contract rather than only in the code. Document a default and a maximum, then clamp anything larger instead of trying to serve it. An endpoint that honours a request for a million rows will be asked for a million rows, usually by a well-meaning client with a retry loop.
Is rate limiting an infrastructure concern or an API design concern?
The enforcement often sits in a gateway and the decision never does. Which operations are expensive, what a fair budget looks like and what a caller is told when they exceed it are properties of the interface, and a gateway cannot infer any of them. Design the limit and then choose where to enforce it.
How large should an API request body be allowed to be?
Large enough for the documented use and no larger, with the limit enforced before the body is parsed. A healthy service refuses an oversized body with 413 Content Too Large rather than accepting it or failing with a 500. Upload and bulk import endpoints need their own higher limit rather than raising the limit everywhere.
Last reviewed by The Routebase Team.