Articles / Broken Object Property Level Authorization: Mass Assignment and Its Twin
OWASP API3 · Broken Object Property Level Authorization

Broken Object Property Level Authorization: Mass Assignment and Its Twin

The 2023 OWASP API list folded two well-known vulnerabilities into one category, and the merger makes sense once you see the shared root. Broken Object Property Level Authorization is what happens when an API enforces access at the level of the whole object but not the individual properties inside it — reading fields a user shouldn't see, or writing fields a user shouldn't set.

API3 combines excessive data exposure and mass assignment. They look like opposites — one is about output, the other about input — but both stem from the same shortcut: serialising or binding an entire object without deciding, property by property, what this particular user is allowed to read or change.

Excessive data exposure: returning too much

writes every fieldserialised backRequest body{ name, is_admin:true }Framework binds bodystraight to the modelPersisted recordprivileged field setResponsereturns whole objectMass assignment:writes fields neverexposedExcessive exposure:password_hash,is_admin returned
Bind or serialise the whole object and the property-level check is never asked.

The classic pattern is an endpoint that fetches a record and returns it whole, trusting the client to display only the relevant fields. A user-profile endpoint returns the profile — including password_hash, is_admin, internal risk scores, or another user's email — because the developer relied on the frontend to hide them. The data is right there in the JSON; hiding it in the UI hides nothing from anyone reading the response.

A profile response that leaks properties the UI never showsGET /api/users/me → { "name": "Ada", "email": "ada@x.io", "password_hash": "$2b$...", "is_admin": false, "internal_risk": 0.82 }

Mass assignment: accepting too much

Mass assignment is the mirror image. Many frameworks bind an incoming JSON body straight onto a model object, so PATCH /api/users/me with {"name":"Ada"} updates the name. But if the code binds the whole body, an attacker can add {"is_admin": true} or {"balance": 999999} and the framework will dutifully write those properties too — privilege escalation delivered by a field the API never intended to expose for writing.

A benign-looking update that escalates privilegePATCH /api/users/me { "name": "Ada", "is_admin": true, "account_tier": "enterprise" }
Why it's one category: Read and write are two faces of the same failure — the API authorises the object but not its properties. Fix the mindset once and you close both: decide, per field, what this user may see and set.

Closing both halves

  • Never serialise objects wholesale. Return an explicit response schema — an allow-list of fields — rather than the raw database model. If a property isn't named, it doesn't ship.
  • Bind inputs to an allow-list too. Accept only the properties a given operation is meant to change; reject or ignore everything else. Never bind a request body directly onto a persistence model.
  • Authorise sensitive properties individually. Fields like role, is_admin, or balance need their own checks — whether the caller may read them and whether they may write them are separate questions.
  • Use schema validation at the boundary. A strict request and response contract, enforced automatically, turns property-level authorization from a per-developer habit into an invariant.
  • Test with extra fields. Replay requests with unexpected properties added and confirm they are rejected, not silently persisted.

API3 is a discipline of specificity: every object crossing the boundary should carry only the properties this user is entitled to, in whichever direction it travels. Generic serialisation and generic binding are convenient precisely because they don't ask the property-level question — which is why the answer defaults to "everything". Knowing which endpoints hand back or accept whole objects is the first step, and you can't audit fields on endpoints you haven't mapped.

Test for it — in practice
  • Read one object back and scan the JSON for fields the caller should never see — internal flags, other users' data, role or isAdmin. Over-exposed properties are half the bug.
  • On an update call, add a property the client shouldn't control (such as "role":"admin") and see if the server accepts it. Silent acceptance is mass assignment.

The full, at-scale version — auditing every response and writable field across the whole API surface for property-level authz gaps — is what our assessment (and SecStudio agents) runs for you.

Keep reading
BOLA: The API Flaw That Tops the OWASP List