Broken Object Property Level Authorization (BOPLA)
Stop exposing or allowing modification of object fields a user should not see or edit — mass assignment and excessive data exposure.
URL: trusteed.io/academy/api-security/broken-object-property-level-authorization
Broken Object Property Level Authorization (BOPLA) occurs when an API exposes or allows modification of individual object properties (fields) that a given user shouldn't be able to see or change — even when the user is correctly authorized to access the object overall. It merges what were two separate categories in the 2019 OWASP list (Excessive Data Exposure and Mass Assignment) into one, because both stem from the same root cause: authorization applied at the object level, but not enforced field-by-field.
What Is BOPLA?
BOPLA has two directions. Excessive data exposure happens when a GET response returns more fields than the client needs or should see — internal notes, password hashes, other users' partial data nested in a response — because the backend serializes the entire database object instead of an explicit, filtered view. Mass assignment happens on the write side: when a PUT/PATCH/POST handler binds the entire request body directly onto a database model, a client can set fields they were never supposed to control — most dangerously, a role or isAdmin field the UI never exposes but the API happily accepts.
Why AI Coding Assistants Get This Wrong
Model.update(req.body) and res.json(user) are the shortest possible implementations of "update a user" and "return a user" — one line each, functionally correct, and exactly what a fast, working demo needs. An assistant generating an update handler has no way to know which fields are supposed to be client-writable unless the schema for writable fields is stated separately from the schema for stored fields. Left unconstrained, the natural completion binds everything.
Vulnerable Pattern
// Mass assignment — the client can set ANY field, including role
app.patch('/api/users/:id', authenticate, async (req, res) => {
const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(user); // and excessive data exposure — returns the full document, hash included
});
// An attacker's request body — the API accepts fields the UI never sends
{
"name": "Jane Doe",
"role": "admin",
"isVerified": true
}
Secure Pattern
// Explicit allow-list on write; explicit projection on read
const UPDATABLE_FIELDS = ['name', 'email', 'phone']; // never 'role', 'isAdmin', 'isVerified'
function pick(obj, keys) {
return Object.fromEntries(keys.filter(k => k in obj).map(k => [k, obj[k]]));
}
app.patch('/api/users/:id', authenticate, async (req, res) => {
const updates = pick(req.body, UPDATABLE_FIELDS);
const user = await User.findOneAndUpdate(
{ _id: req.params.id, _id: req.user.id }, // ownership check (see API1: BOLA)
updates,
{ new: true }
).select('name email phone createdAt'); // explicit projection — no passwordHash, no internal fields
res.json(user);
});
The same discipline applies in reverse for schema/serializer-based frameworks (Django REST Framework, Rails, NestJS DTOs): define an explicit "writable fields" schema for input and an explicit "public fields" schema for output — never serialize or deserialize the raw model.
Copy-Paste Rules for Cursor, Claude Code, and Codex
---
description: BOPLA prevention — field-level authorization on read and write
alwaysApply: true
---
# Object Property Level Authorization Rules
- Never bind an entire request body directly onto a database model
(`Model.update(req.body)`, `Model.create(req.body)`). Always use an explicit
allow-list of client-writable fields, defined separately from the storage schema.
- Fields controlling privilege, role, ownership, verification status, or billing
state (`role`, `isAdmin`, `isVerified`, `balance`, `tenantId`) must NEVER be
settable from client-supplied input, regardless of the user's own role,
unless the endpoint is explicitly an admin-only privilege-management endpoint.
- Never return a full database document/model directly in an API response.
Always define an explicit response projection/DTO/serializer per endpoint
that lists exactly which fields are returned.
- Sensitive fields (password hashes, internal notes, other users' partial data,
security tokens, API secrets) must never appear in any client-facing response,
including nested/related objects returned as part of a larger payload.
- When two user roles see the "same" endpoint but should see different fields
(e.g., admin vs. regular user viewing a profile), implement role-aware
projections explicitly — do not filter fields client-side only.
- When generating an update/create handler, always define the allow-list of
writable fields in the same pass, before writing the persistence call.
Verification Checklist
- No endpoint binds
req.bodydirectly onto a model without an explicit allow-list - No endpoint returns a raw database document without an explicit field projection
- Privilege-related fields (role, admin flags, balances, tenant IDs) are excluded from general-purpose update endpoints
- Sensitive fields (hashes, tokens, internal notes) never appear in any response payload, including nested objects
- Role-aware responses are tested for both the "sees more" and "should see less" roles
- Automated API testing checks for unexpected accepted fields on write endpoints — see Trusteed's WAAP platform for continuous API schema and behavior testing
Frequently asked questions
What is mass assignment, specifically?
Mass assignment is the write-side half of BOPLA: binding an entire request payload onto a model without restricting which fields the client can actually set, allowing an attacker to set fields (like a role or admin flag) that were never intended to be client-controlled.
Is returning "extra" data really a security vulnerability if the UI just ignores it?
Yes. Data returned in an API response is available to anyone inspecting network traffic, regardless of whether the UI displays it — excessive data exposure through unused response fields is a common source of accidental PII and internal-data leaks.
Do GraphQL APIs have the same BOPLA risk?
Yes, and often more acutely, since GraphQL's field-selection model makes over-fetching easy to request. Field-level authorization needs to be enforced in resolvers, not assumed to be handled by the schema shape alone.