API responses contain too much data
CWE-213CWE-200OWASP A01:2021Updated September 4, 20264 min read
Many APIs return the complete database record and leave it to the frontend to decide what is shown. What stays invisible on screen is still present in the response: password hashes, internal notes, other users' data. Filtering belongs on the server.
When building an API it is tempting to return the whole record and let the frontend pick what becomes visible. It saves work, it makes the API flexible, and on screen nobody sees the difference. In the network traffic they do, and that is where anyone who wants to look ends up. What rides along is usually more than anyone intended, and it can be closed off structurally.
What is returning too much data?
We speak of too much data in API responses when an endpoint returns more fields than the requester needs or may see, on the assumption that the client will filter. The term used internationally is excessive data exposure, nowadays grouped under authorisation at property level.
The mistake lies in the assumption about who uses the API. During development that is your own frontend, which neatly displays only the intended fields. But the API is open to anyone with a valid account, and the response is visible in every browser’s developer tools. Everything you send along is therefore handed over.
Compare it with a file whose sensitive pages you cover with a sheet of paper at the moment you show it to someone. As long as the other person keeps looking where you point, all is well. But you handed them the whole file, and the sheet is not glued down.
What does such an API response look like?
Vulnerable:
// The complete record goes out
app.get('/api/users/:id', requireLogin, async (req, res) => {
const user = await db.users.find(req.params.id);
res.json(user);
});
The interface shows a profile card with a name and a photo. The response contains considerably more:
{
"id": 4471,
"name": "J. Walker",
"email": "j.walker@company.com",
"password_hash": "$2b$12$c8y3Nq0Wl0mVQe1n6JbEZe...",
"phone": "+31 6 12345678",
"national_id": "123456782",
"salary_grade": 11,
"internal_note": "Performance review running until Q3",
"mfa_secret": "JBSWY3DPEHPK3PXP",
"role": "employee"
}
Several things go wrong at once here. The password hash is material for an offline cracking attempt. The mfa_secret makes it possible to generate valid two-factor codes yourself, bypassing that whole measure. The internal note and salary grade are personal data not even intended for the person concerned. And because this is one endpoint working on an identifier, the whole set is easy to walk through systematically.
Safe:
// State explicitly what may go out, per role
function publicProfile(user) {
return {
id: user.id,
name: user.name,
department: user.department,
};
}
function ownProfile(user) {
return {
...publicProfile(user),
email: user.email,
phone: user.phone,
};
}
app.get('/api/users/:id', requireLogin, async (req, res) => {
const user = await db.users.find(req.params.id);
if (!user) return res.sendStatus(404);
const own = user.id === req.user.id;
res.json(own ? ownProfile(user) : publicProfile(user));
});
The decisive difference is the direction of the choice. You record what may go out, instead of what has to be left out. That matters more than it seems: with a list of exceptions, every new database field automatically ends up in the response, and nobody thinks of that when adding a column.
Beyond that, only query what you need. A query avoiding SELECT * cannot accidentally send the sensitive fields along, because they are simply not there.
What is the impact of excessive data in API responses?
The severity runs from medium to high and is determined by the leaked fields. If it concerns an internal sequence number, the effect is slight. If it concerns personal data, password hashes or two-factor secrets, it is serious.
The awkward part is that the leak stays entirely invisible in the usual checks. There is no error, no unusual behaviour and no odd pattern in the logs; the endpoint does exactly what it was told. The finding surfaces during a test, or because the data turns up somewhere else.
Combined with other weak spots the impact grows quickly. If object level authorization is missing too, an attacker can retrieve the complete user administration including all sensitive fields. If a rate limit is missing, that happens in minutes. What are separately three medium findings together become a data breach subject to mandatory notification.
How do you detect excessive data in API responses?
A tester looks not at the screen but at the network traffic. Every API response is compared with what the interface shows of it; the difference is the finding. That often yields immediate results, because the discrepancy is rarely small.
They also watch for fields absent from the interface but whose names give something away: anything containing hash, secret, token, internal or note. List endpoints get examined too, where the same object often comes back in a different shape from a single-item request, and where the filtering has then just not been applied. Error responses and nested objects get attention as well, and with GraphQL, fields present in the schema that the frontend never requests. AssistSec assesses whether the fields differ per role, because an admin view and a user view are often served by the same endpoint in practice without any distinction in the output.
How do you prevent excessive data in API responses?
- State explicitly which fields an endpoint returns rather than which you leave out.
- Define an output model per role and serialise through it.
- Query only the columns you need; avoid
SELECT *. - Never return password hashes, two-factor secrets, recovery tokens or API keys.
- Treat nested objects with the same care as the main object.
- Never leave filtering to the frontend; that is presentation, not security.
- With GraphQL, authorise per field, not only per query.
- Check on every new database field whether it unintentionally appears in existing responses.
- Include an output check in your automated tests, so a new field stands out.
Sources
Frequently asked questions
The user does not see those fields in the interface, do they?
Not in the interface, but they are in the response. Anyone can inspect the network requests of their own browser or call the API directly. What the frontend hides is purely a presentation choice; it is not security and it takes two clicks to bypass.
How do I prevent this structurally?
By stating explicitly which fields go out rather than which you leave out. Define an output model per endpoint and per role, and serialise through it. A new database field then does not automatically appear in the response, which is exactly the failure mode you want to rule out.
Does this apply to GraphQL too?
Yes, with its own emphasis. In GraphQL the client decides which fields to request, so authorisation has to happen per field. A field present in the schema but not secured is retrievable, even if your own frontend never asks for it.
Is this the same as a data breach?
It is the cause of one, as soon as someone notices. Often the finding only surfaces during a test or after data turns up elsewhere. What is remarkable is that nothing was broken into: the data was simply handed to anyone who asked.
Related articles
- VulnerabilitiesCWE-285A01:2021Insufficient function level authorizationAn admin function merely hidden from the menu stays reachable through a direct request. Learn how to enforce authorisation per function.
- VulnerabilitiesCWE-639A01:2021Insufficient object level authorization in APIsAn API that only checks whether you are logged in, not whether this record is yours, hands over other people's data. Learn how to enforce it.
- VulnerabilitiesCWE-200A01:2021Information disclosureInformation disclosure explained: how stack traces, .git directories, source maps and over-sharing API responses leak data, and how to stop it.
- VulnerabilitiesCWE-915A01:2021Mass assignmentMass assignment explained: how binding a whole request body onto a model makes isAdmin or balance writable, and how an allowlist prevents it.
- VulnerabilitiesCWE-770A04:2021No rate limiting on the APIWithout rate limiting an API can be queried without end. Learn how that leads to data theft, cost abuse and outages.