Card 04/ 10

ConceptDifficulty: Intermediate2 min

Mass Assignment and Excessive Data Exposure

Binding a body straight to a record lets a client set isAdmin, and returning it shows too much.

Mass assignment happens when an API copies every field of a request body onto a stored record, so a client can set fields it was never meant to change, such as a role or a balance. Excessive data exposure is the same mistake in the other direction: returning the whole stored record, so the response shows fields the caller should not see. OWASP groups both as broken object property level authorisation, and the fix for both is a list of the fields each operation allows.

A PATCH carrying a role field, accepted by a bound record and refused by an allow-list

Body bound to the recordrole changed

PATCH /users/me with {"displayName": "Maria", "role": "admin"} copies both fields onto the user. The response is 200 OK, and the caller is now an administrator.

Allow-list of fieldsrole refused

The operation accepts only displayName, email and locale. The body contains role, so the API answers 400 Bad Request naming the field, or ignores it, and the user's role does not change.

What goes wrong. Frameworks make it easy to bind a request to a model, and the model has every column: role, emailVerified, creditBalance, ownerId. Any of them a client adds to the body is written.

A request that shows it. The PATCH above, or POST /orders with "price": 0. For exposure, GET /users/7 returning passwordHash, internalNotes or another user's email in a list.

The fix. Give each operation its own input type naming only the fields a caller may set, and validate the body against it; reject or drop anything else. Give each response its own output type naming only the fields a caller may see. The contract, such as an OpenAPI schema with additionalProperties: false, can enforce the input side.

How to test. Send every writable endpoint a body with extra fields taken from the stored model, and check none of them changed. Compare every response against the documented fields, and fail on anything extra.

So never pass a request body or a stored record straight through; name the fields each way.