Mass Assignment Vulnerabilities in Laravel Applications

June 10, 2020

Eloquent like many other ORMs have a nice feature that allows assigning properties to an object without having to assign each value individually, this is a nice feature that saves a lot of time and lines of code but can lead to a vulnerability if used incorrectly.

For example here is a simplified example of an insecure code that we found during a security code review for one of our clients

// app/Models/User.php file
class User extends Authenticatable {
 use SoftDeletes;
 const ROLE_USER          = 'user';
 const ROLE_ADMINISTRATOR = 'administrator';

 protected $fillable = ['name', 'email', 'password', 'role'];

  // ... rest of the code ...
}
// app/Http/Requests/StoreUserRequest.php file
class StoreUserRequest extends Request {
 public function rules() {
   return [
       'name'             => 'string|required',
       'email'            => 'email|required',
       'password'         => 'string|required|min:6',
       'confirm_password' => 'same:password',
   ];
 }
}
// app/Controllers/UserController.php file
class UserController extends Controller {
   public function store(StoreUserRequest $request) {
       $user = new User();
       $user->role = User::ROLE_USER;
       $user->fill($request->all());
       $user->save();

       return response()->json([
         'success' => true,
       ],201);
   }

   // ... rest of the code ...
}

The issue here is if a malicious attacker submits the payload below, he will be able to register an admin user with higher privileges and probably take over the admin panel of the application.

{
    "name"             : "Hacker",
    "email"            : "hacker@example.com",
    "role"             : "administrator",
    "password"         : "some_random_password",
    "confirm_password" : "some_random_password"
} 

Also, you might wonder here why role is in the $fillable attribute, if the “role” wasn't there, there would not be a security issue. The reason that it was added there is that there was another API that would allow changing role too, this is a common pattern that we see in Laravel applications that we review or pen-test, while $fillable is great if you have a simple application, it gets hard to manage when application growths and there are multiple APIs(or Roles) that are updating/creating a same type of model with different ACL roles.

Here are a few tips on how you can prevent mass assignment vulnerabilities in Laravel applications.

Prevention tips

1. Pass to model only fields that have been validated

This is probably the most effective method of dealing with mass assignment attacks, instead of passing the full data from request, you can pass only fields that have been validated. In the above code example that will be name, email and password. To do so, Laravel provides you with a $request->validated() method, that returns you only fields that have been validated. So in the code above, replacing $request->all() with $request->validated() would fix the issue.

public function store(StoreUserRequest $request) {
   $user = new User();
   $user->role = User::ROLE_USER;
   $user->fill($request->validated());
   $user->save();

   return response()->json([
     'success' => true,
   ],201);
}

If you are not using Laravel’s request validation, you can also use $request->validate() or $validator->validated() which also returns only data that has been validated.

2. Use whitelisting instead of blacklisting

We encourage using $fillable(which is whitelisting only columns that can be mass assigned) instead of $guarded(defines properties that can’t be mass assigned) because you may easily forget to add a new column to a $guarded array, leaving it open for mass assignment by default.

3. Use $model->forceFill($data) method with caution

forceFill ignores all the config that is set in $guarded and $fillable properties and saves passed data as it is into the model. If you have to use forceFill, make sure passed data can not be manipulated by the user.

📝 If you have a Laravel application that is running on production, check out also our Laravel applications security testing service.

Contact us today
for a free consultation.
Do not delay when it comes to security.
Contact us today for a free consultation.
    Thanks for contacting us!
    We will be in touch with you shortly.