Mass Assignment
Protection against Mass Assignment
Mass Assignment Protection is one of the most important security features of Eloquent that prevents accidentally assigning sensitive fields. This system allows you to specify which fields are assignable and which are not.
Mass Assignment Problem:
Without protection, an attacker can assign sensitive fields like is_admin, role, balance through form input or API request.
Laravel Solution:
Laravel provides two methods for protection against mass assignment:
1. $fillable: List of fields that are assignable (whitelist approach)
2. $guarded: List of fields that are not assignable (blacklist approach)
$fillable (Whitelist):
- Only defined fields are assignable
- More secure as it's explicit
- Suitable for models with few fields
$guarded (Blacklist):
- All fields are assignable except defined ones
- Suitable for models with many fields
- Should be used with caution
$hidden:
- Fields that are not shown in JSON serialization
- For sensitive data like passwords
Best Practices:
- Always use $fillable or $guarded
- Use $fillable for security-critical models
- Use $guarded only for trusted models
- Use $hidden for sensitive fields
Examples
Using $fillable
<?php
class User extends Model
{
protected $fillable = [
'name',
'email',
'password',
'phone',
'address'
];
}
// Allowed
User::create([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => Hash::make('secret')
]);
// Blocked (is_admin not in fillable)
User::create([
'name' => 'John Doe',
'is_admin' => true // This will be ignored
]);
Using $fillable for whitelist approach which is more secure.
Using $guarded
<?php
class User extends Model
{
protected $guarded = ['id', 'is_admin', 'role', 'balance'];
}
// Allowed (not in guarded)
User::create([
'name' => 'John Doe',
'email' => 'john@example.com'
]);
// Blocked (is_admin in guarded)
User::create([
'name' => 'John Doe',
'is_admin' => true // This will be ignored
]);
Using $guarded for blacklist approach.
Using $hidden
<?php
class User extends Model
{
protected $fillable = ['name', 'email', 'password'];
protected $hidden = [
'password',
'remember_token',
'api_token'
];
}
$user = User::find(1);
// Hidden in JSON
$user->toJson(); // password, remember_token, api_token not included
// Hidden in Array
$user->toArray(); // password, remember_token, api_token not included
// But accessible directly
$user->password; // Still accessible
Using $hidden to hide sensitive fields in JSON/Array serialization.
forceFill() for Trusted Data
<?php
class User extends Model
{
protected $fillable = ['name', 'email'];
}
// Normal fill (respects fillable)
$user = new User();
$user->fill(['name' => 'John', 'is_admin' => true]);
// is_admin will be ignored
// Force fill (bypasses protection - use with caution!)
$user = new User();
$user->forceFill([
'name' => 'John',
'is_admin' => true // This will be set
]);
// Only use forceFill for trusted data!
$user->forceFill([
'is_admin' => true
]); // Only if you trust the source
Using forceFill() to bypass protection only for trusted data.
Mass Assignment in Update
<?php
class User extends Model
{
protected $fillable = ['name', 'email', 'phone'];
}
$user = User::find(1);
// Update with mass assignment
$user->update([
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'is_admin' => true // This will be ignored
]);
// Update specific field (bypasses protection)
$user->is_admin = true;
$user->save();
// Or use forceFill
$user->forceFill(['is_admin' => true])->save();
Mass assignment in update operations that follow the same rules.
$fillable and $guarded Together
<?php
// Don't use both together!
// If both are defined, $fillable takes precedence
class User extends Model
{
protected $fillable = ['name', 'email'];
protected $guarded = ['id', 'is_admin'];
// $fillable takes precedence - only name and email are assignable
}
// Best practice: use only one
class User extends Model
{
// Option 1: Use fillable (recommended)
protected $fillable = ['name', 'email', 'password'];
// Option 2: Use guarded
// protected $guarded = ['id', 'is_admin', 'role'];
}
Important note: Using $fillable and $guarded together is not recommended. $fillable takes precedence.
Dynamic $fillable
<?php
class User extends Model
{
protected $fillable = ['name', 'email'];
public function setFillable(array $fillable)
{
$this->fillable = $fillable;
}
public function addFillable($field)
{
$this->fillable[] = $field;
}
}
// Usage (use with caution!)
$user = new User();
$user->setFillable(['name', 'email', 'phone']);
$user->fill(['name' => 'John', 'phone' => '123456']);
Dynamic $fillable that should be used with caution.
Use Cases
- Protection against mass assignment vulnerabilities
- Controlling assignable fields in create/update
- Hiding sensitive fields in JSON responses
- Defining explicit field permissions
- Security hardening for models
Common Mistakes
- Forgetting to define $fillable or $guarded
- Using $guarded = [] which allows all fields
- Using forceFill() for user input
- Forgetting $hidden for sensitive fields
- Using $fillable and $guarded together
- Forgetting to update $fillable/$guarded after schema changes
Best Practices
- Always use $fillable or $guarded
- Use $fillable for security-critical models
- Use $guarded only for trusted models
- Use $hidden for sensitive fields
- Use forceFill() only for trusted data
- Update $fillable/$guarded after schema changes
- Use validation in addition to mass assignment protection
Edge Cases
- Models with dynamic $fillable/$guarded
- Mass assignment with nested relationships
- forceFill() with user input
- $fillable/$guarded with polymorphic relationships
- Mass assignment in queue jobs
- Mass assignment with API resources
Performance Notes
- Mass assignment protection overhead is very low
- $fillable check is faster than $guarded
- $hidden only affects serialization
- Don't use forceFill() for performance-critical operations
Security Notes
- Always use $fillable or $guarded
- Use forceFill() only for trusted data
- Use $hidden for sensitive fields
- Ensure user input is validated
- Don't use mass assignment for user input
- Ensure API endpoints are protected
Interview Points
- What is Mass Assignment and why is it important?
- What is the difference between $fillable and $guarded?
- When should you use $fillable and when $guarded?
- What is $hidden and how does it differ from $fillable/$guarded?
- What is forceFill() and when should it be used?
- How can you test mass assignment vulnerability?
Version Notes
- Laravel 11.x: Improved performance in mass assignment checks
- Laravel 11.x: Better support for nested mass assignment
- Laravel 10.x: Improved $hidden functionality
- Laravel 9.x: Improved forceFill() behavior