Broken Access Control: The Complete Practical Guide
Mass Assignment (Auto-Binding) Vulnerabilities
How mass assignment works: auto-binding extra fields the UI never sent, adding isAdmin or role to a request, seizing ownership, and discovering bindable fields.
Published
Mass assignment is a quiet privilege escalation hiding inside ordinary create and update features. A framework binds the whole request body onto a model, so if the model has a sensitive field like role or isAdmin, you set it by just including it in the request. The form never showed that field, but the server happily accepts it.
What auto-binding does and why extra fields sneak in
Many frameworks offer a convenience: take the incoming body and map every field onto the object. One line of code turns {"name":"...","email":"..."} into an updated record. The trouble is that the same one line also maps {"role":"admin"} if you include it, because it binds everything, not just the fields the form was supposed to send. The vulnerability is the absence of an allow-list on what may be bound.
Adding isAdmin or role to a body the UI never sent
The highest-value test is to add a privilege field to a legitimate request:
POST /api/profile
{"name":"me","email":"[email protected]","role":"admin"}
POST /account/settings
{"username":"me","isAdmin":true,"accountType":"enterprise"}Your update does what it should, and also sets the field the UI never exposed. This is the mass-assignment path to administrator, and it overlaps directly with privilege escalation.
Setting ownership and foreign-key fields
Beyond privilege, mass assignment can seize records by setting ownership or foreign-key fields:
POST /api/documents
{"title":"mine","ownerId":1} (create a document owned by someone else)
PUT /api/orders/1003
{"userId":52} (reassign an order to your account)If the model exposes an ownerId, userId, or teamId and the code binds it, you decide who owns what.
Discovering bindable fields
You cannot set a field you do not know about, so collect field names:
- Read a GET on the same object, which usually returns its full property set, sensitive fields included.
- Check API docs, schema files, and error messages, which leak field names freely.
- Compare a create response to what you sent, since extra properties in the response hint at bindable inputs.
Then add each candidate to a write request and see which ones take.
JSON versus form-encoded nesting
The same attack works in different encodings, and nesting sometimes reaches fields a flat body cannot:
Form-encoded: name=me&[email protected]&role=admin&isAdmin=true
Nested JSON: {"user":{"name":"me","permissions":["*"]}}If a flat role is rejected, try a nested shape that matches the object graph, since binding logic sometimes guards the top level but not nested objects.
The fix and the mindset
The defense is an explicit allow-list of bindable fields, so role, isAdmin, and ownership keys can never be set from the request, never auto-bind the whole body. As an attacker, treat every create and update endpoint as a chance to set a field the form hid, and read the object's own GET response to learn what to try. Mass assignment is a frequent ingredient in account-takeover chains. Practice it on a free live target.
← Back to Broken Access Control: The Complete Practical Guide