Description
Broken Object Property Level Authorization (BOPLA) is a class of access control failure where an API endpoint correctly verifies that a user may access a resource, but fails to verify which properties of that resource the user is permitted to read or write. This creates two distinct attack patterns: over-permission on write operations (mass assignment), where users inject properties into update requests that they should not be able to modify; and over-permission on read operations (excessive data exposure), where responses include sensitive fields the user should not see.
CWE-285 (Improper Authorization) applies because the authorization decision is incomplete—the API checks "can this user access this object?" but not "can this user modify this specific property?" OWASP API Security Project introduced BOPLA as A03 in the 2023 revision, combining what were previously separate mass assignment and excessive data exposure categories into a unified property-level authorization concept.
Mass assignment vulnerabilities are particularly common in frameworks that automatically bind request body parameters to model attributes (Rails, Laravel, Django, NestJS). Without explicit allowlisting of writeable fields, a user who adds "is_admin": true or "account_balance": 999999 to a profile update request may have those values persisted to the database.
How It Works
A normal user updates their profile name:
PATCH /api/v1/users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer <user_token>
Content-Type: application/json
{"name": "Alice Johnson"}
HTTP/1.1 200 OK
{"id": 12345, "name": "Alice Johnson", "is_admin": false}
The attacker adds privileged fields to the same request:
PATCH /api/v1/users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer <user_token>
Content-Type: application/json
{
"name": "Alice Johnson",
"is_admin": true,
"role": "superadmin",
"account_balance": 99999,
"email_verified": true
}
HTTP/1.1 200 OK
{"id": 12345, "name": "Alice Johnson", "is_admin": true}
If the server uses an ORM that mass-assigns request body parameters directly to the model without an explicit allowlist, the privileged fields are written to the database. The attacker has escalated to administrator without any authentication bypass.
In Rails, vulnerable code looks like:
# Vulnerable: mass assignment via strong parameters omission
def update
@user.update(params[:user]) # All params accepted
end
# Fixed: explicit allowlist
def update
@user.update(user_params)
end
def user_params
params.require(:user).permit(:name, :email) # Only name and email
end
Testers systematically add every field observed in API responses to update requests, observing whether the server accepts and persists them.
Impact
- Privilege escalation — Users set
is_admin,role, orpermissionsfields to gain unauthorized administrative access. - Financial fraud — Account balance, credit limit, or discount rate fields are modified to benefit the attacker.
- Account verification bypass — Fields like
email_verified,kyc_status, orsubscription_tierare set to bypass business controls. - Data integrity violation — Modification of internal audit fields (
created_at,last_modified_by) corrupts audit trails. - Sensitive data leakage — Read-side BOPLA exposes password hashes, internal IDs, and confidential flags to unauthorized users.
Detection
- Intercept a standard object update request (PATCH or PUT) in Burp Suite and add every field observed in the GET response for the same object, then submit and compare the response.
- Specifically test adding
is_admin: true,role: "admin",account_tier: "enterprise", and similar privilege-related fields to update requests. - Compare the response body fields to the request body fields—any fields in the response that were not in the request suggest server-side property access to audit on read.
- Test non-standard HTTP methods: some APIs use PUT (full replacement) rather than PATCH; test full-replacement payloads that include all observed fields.
- Compare the API behavior between different user roles by sending the same requests with standard user and admin tokens, noting which properties each role can write.
Remediation
Use explicit input allowlists. Define serializers or DTOs that enumerate only the properties users may write. Never pass raw request bodies to ORM update methods:
# Django REST Framework — write-specific serializer
class UserUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['name', 'email', 'phone'] # Writeable fields only
read_only_fields = ['is_admin', 'role', 'account_balance']
Apply property-level authorization. For operations that require field-level access differences between roles, implement explicit authorization checks per field in the update handler.
Separate read and write schemas. Use different serializer classes for GET (read) and PATCH/PUT (write) operations. The read serializer may return more fields than the write serializer accepts.
Log and alert on unexpected fields. Detect potential BOPLA exploitation attempts by logging when requests include fields outside the expected schema and alerting on patterns.
