Skip to main content

Mass Assignment

Introduction to Mass Assignment

Software frameworks sometime allow developers to automatically bind HTTP request parameters into program code variables or objects to make using that framework easier on developers. This can sometimes cause harm.

Attackers can sometimes use this methodology to create new parameters that the developer never intended which in turn creates or overwrites new variable or objects in program code that was not intended.

This is called a Mass Assignment vulnerability.

Alternative Names

Depending on the language/framework in question, this vulnerability can have several alternative names:

  • Mass Assignment: Ruby on Rails, NodeJS.
  • Autobinding: Spring MVC, ASP NET MVC.
  • Object injection: PHP.

Example

Suppose there is a form for editing a user's account information:

<form>
<input name="userid" type="text">
<input name="password" type="text">
<input name="email" text="text">
<input type="submit">
</form>

Here is the object that the form is binding to:

public class User {
private String userid;
private String password;
private String email;
private boolean isAdmin;

//Getters & Setters
}

Here is the controller handling the request:

@RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String submit(User user) {
userService.add(user);
return "successPage";
}

Here is the typical request:

POST /addUser
...
userid=bobbytables&password=hashedpass&[email protected]

And here is the exploit in which we set the value of the attribute isAdmin of the instance of the class User:

POST /addUser
...
userid=bobbytables&password=hashedpass&[email protected]&isAdmin=true

Exploitability

This functionality becomes exploitable when:

  • Attacker can guess common sensitive fields.
  • Attacker has access to source code and can review the models for sensitive fields.
  • AND the object with sensitive fields has an empty constructor.

GitHub case study

In 2012, GitHub was hacked using mass assignment. A user was able to upload his public key to any organization and thus make any subsequent changes in their repositories. GitHub's Blog Post.

Solutions

  • Allow-list the bindable, non-sensitive fields.
  • Block-list the non-bindable, sensitive fields.
  • Use Data Transfer Objects (DTOs).

General Solutions

An architectural approach is to create Data Transfer Objects and avoid binding input directly to domain objects. Only the fields that are meant to be editable by the user are included in the DTO.

public class UserRegistrationFormDTO {
private String userid;
private String password;
private String email;

//NOTE: isAdmin field is not present

//Getters & Setters
}

Language & Framework specific solutions

Spring MVC

Allow-listing

@Controller
public class UserController
{
@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request)
{
binder.setAllowedFields(["userid","password","email"]);
}
...
}

Take a look here for the documentation.

Block-listing

@Controller
public class UserController
{
@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request)
{
binder.setDisallowedFields(["isAdmin"]);
}
...
}

Take a look here for the documentation.

NodeJS + Mongoose

Allow-listing

var UserSchema = new mongoose.Schema({
userid: String,
password: String,
email : String,
isAdmin : Boolean,
});

UserSchema.statics = {
User.userCreateSafeFields: ['userid', 'password', 'email']
};

var User = mongoose.model('User', UserSchema);

_ = require('underscore');
var user = new User(_.pick(req.body, User.userCreateSafeFields));

Take a look here for the documentation.

Block-listing

var massAssign = require('mongoose-mass-assign');

var UserSchema = new mongoose.Schema({
userid: String,
password: String,
email : String,
isAdmin : { type: Boolean, protect: true, default: false }
});

UserSchema.plugin(massAssign);

var User = mongoose.model('User', UserSchema);

/** Static method, useful for creation **/
var user = User.massAssign(req.body);

/** Instance method, useful for updating**/
var user = new User;
user.massAssign(req.body);

/** Static massUpdate method **/
var input = { userid: 'bhelx', isAdmin: 'true' };
User.update({ '_id': someId }, { $set: User.massUpdate(input) }, console.log);

Take a look here for the documentation.

Ruby On Rails

Take a look here for the documentation.

Django

Take a look here for the documentation.

ASP NET

Take a look here for the documentation.

PHP Laravel + Eloquent

Allow-listing

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
private $userid;
private $password;
private $email;
private $isAdmin;

protected $fillable = array('userid','password','email');
}

Take a look here for the documentation.

Block-listing

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
private $userid;
private $password;
private $email;
private $isAdmin;

protected $guarded = array('isAdmin');
}

Take a look here for the documentation.

Grails

Take a look here for the documentation.

Play

Take a look here for the documentation.

Jackson (JSON Object Mapper)

Take a look here and here for the documentation.

GSON (JSON Object Mapper)

Take a look here and here for the document.

JSON-Lib (JSON Object Mapper)

Take a look here for the documentation.

Flexjson (JSON Object Mapper)

Take a look here for the documentation.

References and future reading

Mitigation

Here's are the list of the mitigation to prevent vulnerable input validation:

1. Use Allowlist for Assignable Attributes

  • Explicitly define which attributes are safe to assign in models or controllers.
  • Avoid using wildcards to allow unrestricted attribute assignment.

2. Disable Mass Assignment by Default

  • Use frameworks or libraries that disable mass assignment globally.
  • Manually specify attributes to be updated for each operation.

3. Validate Inputs Explicitly

  • Validate and sanitize input data to ensure only valid attributes are processed.
  • Reject requests containing unexpected or additional parameters.

4. Implement Role-Based Attribute Access Control

  • Assign attributes based on user roles and permissions.
  • Restrict access to sensitive fields for unauthorized users.

5. Use Secure ORM Methods

  • Leverage Object-Relational Mapping (ORM) methods that enforce strong typing and attribute controls.
  • Avoid directly mapping user inputs to database fields without validation.

6. Avoid Using update() or save() with Unfiltered Data

  • Do not pass user-provided data directly to methods like update() or save() in frameworks.
  • Use explicit assignment for each trusted attribute instead.

7. Review and Harden API Endpoints

  • Audit API endpoints to ensure they do not expose unintended attributes.
  • Use tools to test for potential mass assignment vulnerabilities.

8. Log and Monitor Suspicious Activity

  • Log attempts to modify restricted attributes.
  • Monitor logs for patterns of abuse or unauthorized access attempts.

9. Train Developers on Secure Coding Practices

  • Educate developers about mass assignment risks and best practices for secure attribute handling.
  • Incorporate secure development guidelines into team workflows.

10. Regular Security Assessments

  • Conduct regular code reviews and penetration tests to identify mass assignment risks.
  • Ensure mitigations remain effective as the application evolves.