Laravel Mass Assignment Vulnerability: How to Protect Eloquent Models with $fillable and $guarded

Eloquent models without $fillable or $guarded allow attackers to set any database column through request input, including is_admin, role, or email_verified_at.

High severity Application Security Updated 2026-05-01

The Problem

Mass assignment occurs when an Eloquent model accepts all request input without restriction. If a model lacks $fillable or $guarded, an attacker can add extra fields to a form submission — like is_admin=1 or role=superadmin — and those values get written directly to the database. This is one of the most common Laravel vulnerabilities and the reason the framework includes mass assignment protection by default.

How to Fix

  1. 1

    Add $fillable to every Eloquent model

    Explicitly list the columns that can be mass-assigned:

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

    Never include sensitive columns like is_admin, role, email_verified_at, or balance in $fillable.

  2. 2

    Or use $guarded as a blocklist

    If you prefer a blocklist approach, set $guarded to protect specific columns:

    class User extends Model
    {
        protected $guarded = [
            'id',
            'is_admin',
            'role',
            'email_verified_at',
        ];
    }
    Warning: $guarded = [] (empty array) disables all protection and is equivalent to having no mass assignment guard at all. Never use this in production.
  3. 3

    Validate and filter input before creating models

    Even with $fillable, always validate input first:

    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
    ]);

    User::create($validated);

    Using $request->validate() returns only the validated fields, providing a second layer of protection beyond $fillable.
  4. 4

    Audit existing models for missing protection

    Search your codebase for models without mass assignment protection:
    grep -rL 'fillable\|guarded' app/Models/

    This lists all model files that don't contain either $fillable or $guarded. Fix each one before deploying.

How to Verify

Test by sending an unexpected field in a request:

curl -X POST https://yourapp.com/api/users \
  -d 'name=Test&email=test@test.com&is_admin=1'

Then check the database — the is_admin column should not be set to 1. Also run the Stackshield scanner to verify: php artisan stackshield:scan --check=SS001

Prevention

Use a Pint or PHPStan rule to flag models without $fillable. Add mass assignment checks to your CI pipeline. Prefer $fillable (allowlist) over $guarded (blocklist) for stronger defaults. Use StackShield to continuously monitor for unprotected models.

Frequently Asked Questions

Is $guarded = [] ever acceptable?

Almost never. Some developers use it during rapid prototyping, but it should never reach production. If you need maximum flexibility, use $guarded with an explicit list of protected columns rather than an empty array.

Does Form Request validation prevent mass assignment?

Partially. Form Requests validate input types and values, but the validated data still goes through Eloquent mass assignment. Both layers are needed: validation ensures correct data types, $fillable ensures only allowed columns are written.

What about Model::unguarded() in seeders?

Model::unguarded() temporarily disables mass assignment protection and is common in database seeders. This is safe in seeders since they run in controlled environments, but never use it in controllers, jobs, or any code that handles user input.

Related Security Terms

Detect This Automatically with StackShield

StackShield continuously monitors your Laravel application from the outside and alerts you when security issues are found. No installation required.

Start Free Trial