Vulnerability

Broken Object Property Level Authorization (BOPLA)
detect, understand, remediate

BOPLA is the field-level authorisation failure inside an object the caller is otherwise allowed to access. The route resolves, the object loads, and the wrong fields come back on a read or get accepted on a write. Ranked third on the OWASP API Security Top 10 (API3:2023), BOPLA absorbs the older Excessive Data Exposure (read side) and Mass Assignment (write side) categories and is one of the quietest paths to silent privilege escalation in modern multi-tenant APIs.

No credit card required. Free plan available forever.

Severity

High

CWE ID

CWE-915

OWASP Top 10

API3:2023 - Broken Object Property Level Authorization

CVSS 3.1 Score

8.1

What is broken object property level authorization (BOPLA)?

Broken object property level authorization (BOPLA) is an API authorisation failure that lives inside an object the caller is otherwise allowed to access. The route resolves, the object loads, and the wrong fields come back on a read or get accepted on a write. Authentication passes. Object-level authorisation passes. Function-level authorisation passes. The check that sits one level deeper, the per-field decision about which properties the principal may see and set on this object in this state, is missing or only partially enforced. The token is valid, the object is theirs, and the field is not.

OWASP ranks BOPLA third on the API Security Top 10 (API3:2023). The 2023 release folded two earlier categories into BOPLA: Excessive Data Exposure (the read side, where the API serialises more fields than the principal is entitled to see) and Mass Assignment (the write side, where the API auto-binds request fields the principal should not be allowed to set). BOPLA is the unified property-level failure that sits beneath both halves.

BOPLA is closely related to mass assignment and to sensitive data exposure, but the OWASP API Top 10 view is more precise. Mass assignment is the writer-side mechanism. Excessive data exposure is the reader-side symptom. BOPLA is the property-level authorisation gap that produces both. A clean modern report writes the finding as BOPLA, names the property, and identifies whether the failure is on the read path, the write path, or both.

BOPLA vs BOLA vs BFLA: where the line sits

The OWASP API Security Top 10 separates three authorisation classes that share a root cause but fail at different layers. BOLA fails at the object-by-ID layer. BFLA fails at the function-by-role layer. BOPLA fails at the property-by-field layer once the caller is already through the first two checks. A clean finding pins the failure to one of the three; a sloppy finding conflates two and the remediation guidance applies to neither.

DimensionBOLA (API1)BFLA (API5)BOPLA (API3)
What failsObject access check by ID.Function or action invocation check by role or scope.Field-level read or write check inside an authorised object.
Typical surfaceGET, PUT, DELETE on /resource/:id with another tenant's ID.POST, PUT on /admin, /internal, /reports reachable from a non-admin token.GET responses that leak admin-only fields. PATCH that sets isAdmin, role, tenant_id, balance, status when those fields should be server-controlled.
CatalogOWASP API1:2023, CWE-639 / CWE-285.OWASP API5:2023, CWE-285.OWASP API3:2023, CWE-915 (write side) / CWE-213 (read side).
Typical impactCross-tenant data exposure or modification through automated ID enumeration.Vertical privilege escalation, admin actions from a normal account, tenant isolation breach.Quiet self-promotion to admin, silent change of tenancy or status, leakage of admin-only fields, balance or pricing manipulation.
Where the fix livesDatabase query with tenant or owner predicate, plus authorisation policy on the read path.Per-route role and scope check tied to the verified session, not the URL prefix or UI menu.Per-field allowlist on read serialisation and write binding, parameterised by role and object state.

How it works

1

Authenticate as a regular user

The attacker creates a normal-tier account on the SaaS application. They authenticate, capture a valid bearer token, and call only the endpoints that their role is allowed to call. Object-level and function-level authorisation are intact at this stage.

2

Inspect responses for over-fetched fields

They look at the full JSON returned by their authorised reads. Any field that the principal should not see (other users' email addresses on a /users response, an internal isAdmin flag in a /me payload, billing internals in a /invoice response) is a read-side BOPLA candidate.

3

Send unexpected fields on the write path

They take legitimate PATCH and PUT request bodies and append fields they should not control: role, isAdmin, tenant_id, status, balance, verified, plan_tier, internal_notes. They send the request and watch the response and the next read to see whether the field was silently accepted.

4

Walk every related object

They repeat the same property-level probe across each object type: user, tenant, invoice, document, integration, API key. The attacker who finds one BOPLA on a user record is usually one object type away from the next field-level gap on the next record.

Common causes

Returning the database row directly

The handler runs SELECT * FROM users WHERE id = $1 and returns the row as JSON. Internal columns (password_hash, mfa_secret, billing_token, internal_score, owner_user_id) ride out on every response. The fix is a serialiser that names allowed fields, not a database column hidden by convention.

Auto-binding the entire request body

A framework binder accepts the full JSON body and calls Model.update(body). Any field on the model is now writable, including role, tenant_id, isAdmin, and status. The fix is a per-action allowlist of writable properties, not a deny-list of the fields the team remembers to filter.

Role-aware UI, role-blind API

The front end hides admin-only fields when the user is not an admin. The API still returns those fields and the UI just chooses not to render them. Any caller that bypasses the UI sees the full payload. Field-level authorisation must live at the API serialisation boundary.

Trusting client-supplied state on a write

The API accepts status, plan_tier, or balance from the request body when those values should be derived server-side from a workflow, a payment confirmation, or an admin action. The principal can quietly self-promote, refund, or deactivate.

GraphQL field-level resolvers without checks

The top-level GraphQL query checks authorisation on the object, but nested resolvers (user.role, tenant.billing, document.internalReviewerNotes) load fields from the loaded object without re-checking that the field is visible to the calling principal at the requested role.

One serialiser shared across roles

A single UserSerializer renders the same fields for admin, support, and self responses. The team relies on remembering to switch serialisers per route. Any route that forgets returns the admin-shaped payload to a non-admin principal.

How to detect it

Automated detection

  • SecPortal's authenticated scanner captures the response field set under a high-privilege session, replays the same routes under a low-privilege token, and flags fields that should not have been served, surfacing read-side BOPLA gaps before manual triage starts.
  • Property-injection probes append fields like role, isAdmin, tenant_id, status, balance, and verified to legitimate PATCH and PUT bodies, then re-read the object to detect silent acceptance, surfacing write-side BOPLA on POST, PUT, and PATCH paths.
  • Code scanning surfaces serialisers that emit the full model and binders that accept the full request body, anchoring the BOPLA finding to a specific file and line in the connected repository so the systemic pattern is visible alongside the proven instances.

Manual testing

  • Provision two roles in the same tenant: a regular user and an admin. Compare the field set returned to each role on every shared endpoint. Any field that appears under the regular user but should be admin-only is a read-side BOPLA candidate.
  • Take every PATCH and PUT body the regular user can send and append role, isAdmin, tenant_id, status, balance, verified, plan_tier, and internal_notes. Send the request and re-read the object to see whether the appended field was silently accepted.
  • Walk GraphQL field-level resolvers explicitly. Request user.role from a non-admin context. Request tenant.billing from a cross-tenant context. Request document.internalReviewerNotes from a viewer principal. Each resolver is a separate authorisation decision.
  • Inspect mobile clients and SPA bundles for fields the UI never renders. Anything the front end ignores but the API still returns is a candidate over-fetch. Anything the front end never sends but the binder still accepts is a candidate over-write.

How to fix it

Allowlist properties at serialisation, not at the database

Every read path should hand the loaded object to a serialiser that names the fields the principal is allowed to see, given the role and the object state. The serialiser refuses to emit fields outside the allowlist. The database row stays whole; the response is shaped at the API boundary by an explicit policy.

Allowlist properties at the binder, not at the model

Every write path should hand the request body to a binder that names the fields the principal is allowed to set, given the role and the action. The binder drops every other field. Auto-binding the entire request body to the model is the single most common BOPLA mechanism.

Parameterise the allowlist by role and object state

Allowed fields differ between roles, between tenants, between object lifecycle stages, and between user-on-self versus user-on-other. The allowlist is a function of the actor, the action, and the object state, not a single static list. Pulling that policy into a named function makes the decision auditable.

Test BOPLA in CI/CD across roles and object states

Add integration tests that authenticate as a regular user, send the full admin-shaped write body, and assert that the protected fields are dropped or the request is rejected. Add tests that read the same object under different roles and assert that the field set differs as documented. New endpoints inherit the suite by attribute or path matching.

Reject writes that include unknown fields

Configure the binder to reject (rather than silently drop) any field outside the allowlist. A request that tries to set role on a profile update should fail loud, not succeed quietly with the field ignored. Loud failure exposes probing during testing and CI rather than hiding it.

Log denied property writes and over-fetched reads

A write that included a disallowed field is a behavioural signal. Repeated property injections from one principal across many objects is a probing pattern. Telemetry that captures the field name and the object type makes BOPLA exploitation visible, not just BOPLA presence.

How to report a BOPLA finding

BOPLA findings are easy to demonstrate (one over-fetched response or one silently accepted write field) and structurally hard to remediate (because the fix sits in serialisation and binder policy, not in input validation). A clean report keeps the proof concrete, names the field, and points the remediation to the right architectural layer.

  • Pin the finding to one concrete property on one concrete object. Name the field, the object type, and the role under which the gap was reproduced. Generic descriptions like "the API returns too much data" do not survive remediation discussion.
  • Capture the full response for the read-side finding (so the over-fetched field is visible in evidence) or the request and post-write response for the write-side finding (so the silent acceptance is visible).
  • Score the finding with a CVSS 3.1 vector that reflects the real impact. A self-promotion to admin via a role property change is integrity high, scope changed, often privileges high. A leaked email column is confidentiality low, integrity none. The vector matters.
  • Distinguish single-property BOPLA from systemic BOPLA. A serialiser that emits the full model on every read, or a binder that accepts the full body on every write, is one architectural finding, not many independent issues, and the report should say so.
  • Recommend a per-field allowlist policy at the serialisation and binder layer rather than a per-endpoint patch where the pattern is systemic. A list of patched endpoints does not protect the next object the team ships.

How SecPortal helps with BOPLA findings

SecPortal is the engagement record where authenticated scanner output, manual API findings, and remediation tracking live on the same engagement. For BOPLA specifically, the platform supports the workflow at four points.

  • The authenticated scanner captures the response field set under a high-privilege session, replays the same routes under a low-privilege token, and flags response fields that should not have been served, surfacing read-side BOPLA gaps. Property-injection probes on PATCH and PUT bodies surface write-side BOPLA before manual triage starts.
  • Findings management lets you log a BOPLA finding once with the CVSS 3.1 vector, the OWASP API3:2023 mapping, the captured request and response evidence, the property name, and the remediation guidance from the finding template library, then attach every recurring instance back to the same systemic finding rather than creating duplicate records.
  • Code scanning via Semgrep surfaces serialisers that emit the full model and binders that accept the full request body, anchoring the BOPLA finding to a specific file and line in the connected repository so the systemic pattern sits alongside the proven instances.
  • The branded client portal gives the engineering team the finding, the proof, the property name, and the remediation owner in one place. Retests pair to the original finding so the verification record is anchored to the original scope and the response field set under the new build is compared against the original gap.

For the wider API testing context, see the API security testing checklist and the API security testing use case. For the matching risk taxonomy, see the OWASP API Security Top 10 framework reference. For severity calibration on findings like BOPLA, see the research on severity calibration for pentest findings. AppSec and product security teams running this as part of an ongoing programme can read the AppSec teams view or the product security teams view to see how BOPLA findings sit alongside scanner-result triage, vulnerability prioritisation, and the SDLC handoff.

Where BOPLA fits in the AppSec lifecycle

Design review

The authorisation matrix lists allowed read fields and allowed write fields per role per object state. The output is the serialiser policy and the binder policy the implementation has to honour.

Pull request review

New routes that return objects or accept writes are reviewed against the allowlist. A serialiser that emits the full model or a binder that accepts the full body is a review-blocking issue, not a follow-up ticket.

Pre-release testing

Authenticated scanning compares response field sets across roles and probes property injection on writes. Integration tests assert the serialiser allowlist and the binder allowlist on every shared endpoint.

Production monitoring

Denied property writes and over-fetched reads feed the SOC and the AppSec rotation. Repeated property-injection attempts from one principal across many objects is a probing pattern that warrants investigation.

Incident response

A confirmed BOPLA exploitation triggers the incident response process, scope assessment of every object that shares the same serialiser or binder, and a hold on adjacent releases until the architecture fix is in.

Retest and closure

The retest replays the original probe against the fixed route, asserts the field is no longer emitted or accepted, and walks adjacent objects to confirm the systemic pattern is resolved before closing the finding.

Compliance impact

Catch BOPLA before a stray field changes the role

SecPortal's authenticated scanner replays property-level reads and writes across role and tenant contexts, captures the response field deltas as reproducible evidence, and carries the finding through retest. Start scanning for free.

No credit card required. Free plan available forever.