AP-0308Data exposureSeverity · High

An API response carrying more than the screen shows

The interface shows a name and an avatar; the response behind it also carries the email, the internal identifiers, the role flags and a token. The screen hides the extra fields, and the network tab reveals every one.

At a glance

Controls
OWASP API3:2023 · SOC 2 CC6.1 · ISO 27001 A.8.2
LikelihoodVery common with serialise-the-record APIs
Effort to exploitLow: read the network response
Blast radiusEvery field the query returns
What review catchesLittle: the UI looks correct

A response is only as private as its narrowest field. Products often send the whole database row to the browser and let the interface pick what to show. What the user sees is a tidy profile card; what the response contains is the full record, including fields the design never intended to expose. Hiding a field in the interface does nothing to the bytes on the wire.

Why it happens

The fastest way to build an endpoint is to serialise the object and return it. An assistant asked for a profile endpoint returns the profile object, all of it, because trimming it to the fields the screen needs is extra work nobody specified. As the record grows a new column at a time, each addition quietly joins the response, and the endpoint that once returned a name now returns a name, an email, a password reset token and an internal role.

How the attack unfolds

An attacker opens the network tab and reads the responses the product already sends them. No special access is required, because the data is being handed over on every page load. They look for identifiers to reuse, tokens to replay, email addresses to harvest and role flags to flip, and they check whether the list endpoint returns other people’s records with the same generous shape.

Nobody exfiltrated the data. The product delivered it, formatted, on every request.MATT research team

Why review misses it

The page looks right, so review signs off. The extra fields are invisible unless someone reads the raw response, and the endpoint that leaks the most is often the one that felt too simple to examine. The defect lives in what the response includes, and the interface is designed to make that invisible.

Where it hides

What a reviewer can look for in the code and in the behaviour, before anyone proves it.

  • Endpoints serialise and return whole records, with no explicit list of allowed fields.
  • Fields hidden in the interface are still present in the response body.
  • List endpoints return the same broad shape as detail endpoints, multiplied across records.
  • Tokens, internal identifiers or role flags appear in responses that the screen never shows.

A real instance

A team directory returned each colleague’s full record, including a personal phone number and a password reset token that had not yet been used. The interface displayed only names and photos. Reading a single response gave an attacker a working reset token for another account.

Details altered to protect the customer. Pattern, timing and outcome are as found.

How MATT tests it

The proof runs against the deployed product, on every release, whoever wrote the code.

  1. 01Read every response in the network tab and compare its fields against what the screen displays.
  2. 02Look for identifiers, tokens, email addresses and role flags that the interface never renders.
  3. 03Call list and search endpoints and check whether other people’s records arrive with the same broad shape.
  4. 04Test whether a hidden field can be acted on: a returned token replayed, a role flag flipped on write.

The evidence

Before the fix
  1. GET /api/profile200 OKReturns name, avatar, email, internal id, role, reset token
  2. GET /api/users?team=acme200 OKEvery teammate’s full record, same broad shape
After the fix
  1. GET /api/profile200 OKReturns only the fields the screen displays
  2. GET /api/users?team=acme200 OKPublic fields only, chosen explicitly per endpoint
The rule, in plain English

A response must contain only the fields the screen displays.

Executable check

Every endpoint’s response must validate against an explicit field allow-list, with no token, secret or internal identifier present, on every release.

Every rule MATT holds →

What the fix looks like

  1. 01

    Return an allow-list, not the record

    Serialise an explicit set of fields per endpoint, so a new column joins a response only when someone adds it on purpose.

  2. 02

    Separate read models from storage

    Shape responses for the screen that consumes them, so internal fields never have a path to the client.

  3. 03

    Pin it with a rule

    A response must contain only the fields its screen displays, checked against a schema on every release.

A check that was never written is found by proving the deployed behaviour, fixed with the engineer who shipped it, and kept fixed by running the control on every change. What is ExploitOps? →

ExploitOps discovery call

Understand where your security loop breaks.

A 15-minute call with a MATT co-founder to understand your product and whether an ExploitOps Review is worth doing. If there is a real need, we scope one with the research team.