In Depth: Mass-Assignment Vulnerabilities

[InDepth#15] There is a false confidence about mass-assignment vulnerabilities that hides how easy it is for them to occur and be exploited...

In Depth: Mass-Assignment Vulnerabilities

When you think of Mass-Assignment vulnerabilities on Laravel, I guarantee you’ll immediately think of the humble $fillable Eloquent property. That’s definitely what the Laravel Docs teach you

If you’re not familiar with the $fillable Eloquent property, this is how it works:

First, you define the property on your model:

class User extends Model
{
    protected $fillable = ['name', 'email'];
}

Next, when creating or updating the model, only the allowed attributes can be filled.

$input = [
    'name'  => 'Bilbo Baggins',
    'email' => 'bilbo@example.com',
    'admin' => true
];

$user = User::create($input);

// $user->name  -> 'Bilbo Baggins'
// $user->email -> 'bilbo@example.com'
// $user->admin -> false

In the above example, the admin = true flag is ignored when creating the User model, protecting against a mass-assignment exploit to escalate the privileges of the newly created user.

And this is where many people stop learning about mass-assignment vulnerabilities and assume they are covered.

But are they?

I would argue that $fillable is the lesser, or possibly even optional, protection against mass-assignment. Great to use if you can, but not essential and definitely not my first choice.

In fact, I often allow mass-assignment across all fields of a Model using $guarded = [];, or even Model::unguard() across the entire app in AppServiceProvider. As controversial as that may be, I find $fillable gets in my way more often than it protects me. Disabling the protection actually makes the code cleaner and more maintainable.

So what do I do instead?