API Mass Assignment
OWASP API #6. The framework binds every field in the JSON body to a model attribute. Send is_admin=true or role=admin in a PATCH request — the model writes it without explicit code.
OWASP API #6. The framework binds every field in the JSON body to a model attribute. Send is_admin=true or role=admin in a PATCH request — the model writes it without explicit code.
The framework auto-binds all JSON body fields to model attributes without an explicit allowlist. An attacker sends PATCH /api/users with {"name": "new name", "is_admin": true}— and the framework binds is_adminto the model. This is OWASP API Security Top 10 #6: Mass Assignment.
Mass assignment occurs when a framework binds every field in a JSON request body directly to a database model without checking which fields the client is allowed to set. The classic example is Ruby on Rails, which had a notorious attr_accessible era — but the same pattern appears in Spring Boot, Django REST Framework, ASP.NET, and Fastify.
The key distinction from Excessive Data Exposure (OWASP #3) is direction: exposure is about what the API returns, while mass assignment is about what the API accepts. A single endpoint can suffer from both — returning sensitive fields and accepting unauthorised writes to them.
Edit a user profile with and without mass assignment protection. In vulnerable mode, hidden fields like role and is_admin are bound from the request body. In safe mode, an allowlist strips them.
Edit user profile fields. The form sends a JSON body that may include hidden fields.
"role": "admin" or "is_admin": true"is_admin": true. In vulnerable mode, the framework binds it — in safe mode, the allowlist strips it.In 2012, GitHub suffered a mass assignment vulnerability in its Rails-based API. An attacker could add themselves as a collaborator on any public repository by sending a POST /api/repos/:owner/:repo/collaborators request with additional parameters. The Rails controller used update_attributes(params[:user]) without an allowlist, so the attacker could set their permission level to “admin” on the repository.
The vulnerability was discovered by security researcher Egor Homakov, who responsibly disclosed it after demonstrating the exploit on a public repository. GitHub's fix was to introduceattr_accessible (later strong_parameters) across the entire API surface. The incident became a landmark case study in the Rails community and spurred the adoption of permit-based parameter handling as a default pattern.
The fix is straightforward: never bind request body fields directly to a model. Use an allowlist (permitted parameters) that explicitly lists every field the client is allowed to modify. For read-only or sensitive fields like role, is_admin, or permissions, never accept them from the client — set them server-side based on business logic.
// VULNERABLE — Rails-style mass assignment
def update
@user = User.find(params[:id])
@user.update_attributes(params[:user])
# params[:user] may contain is_admin: true
end
// VULNERABLE — Spring Boot auto-binding
@PutMapping("/users/{id}")
public User update(@RequestBody User user) {
// User object bound from JSON includes all fields
return userRepository.save(user);
}
// SAFE — Rails strong parameters
def update
@user = User.find(params[:id])
@user.update(user_params)
end
private
def user_params
params.require(:user).permit(:name, :email, :bio)
# :role and :is_admin are silently ignored
end
// SAFE — Spring Boot DTO
class UpdateUserDTO(
val name: String?,
val email: String?,
val bio: String?,
// role and isAdmin deliberately absent
)
@PutMapping("/users/{id}")
fun update(@Valid @RequestBody dto: UpdateUserDTO): User {
val user = userRepository.findById(id).orElseThrow()
dto.name?.let { user.name = it }
dto.email?.let { user.email = it }
dto.bio?.let { user.bio = it }
// role is never overwritten from client input
return userRepository.save(user)
}role, is_admin, or permission fields from client input — set them server-side.1.What is the difference between excessive data exposure and mass assignment?
2.Which mitigation is most effective against mass assignment?
3.Why does framework auto-binding make mass assignment a common vulnerability?