Files
laravel-shopping-cart/.claude/skills/laravel-best-practices/rules/security.md
T
2026-04-09 16:06:44 -06:00

3.4 KiB

Security Best Practices

Mass Assignment Protection

Every model must define $fillable (whitelist) or $guarded (blacklist).

Incorrect:

class User extends Model
{
    protected $guarded = []; // All fields are mass assignable
}

Correct:

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

Never use $guarded = [] on models that accept user input.

Authorize Every Action

Use policies or gates in controllers. Never skip authorization.

Incorrect:

public function update(Request $request, Post $post)
{
    $post->update($request->validated());
}

Correct:

public function update(UpdatePostRequest $request, Post $post)
{
    Gate::authorize('update', $post);

    $post->update($request->validated());
}

Or via Form Request:

public function authorize(): bool
{
    return $this->user()->can('update', $this->route('post'));
}

Prevent SQL Injection

Always use parameter binding. Never interpolate user input into queries.

Incorrect:

DB::select("SELECT * FROM users WHERE name = '{$request->name}'");

Correct:

User::where('name', $request->name)->get();

// Raw expressions with bindings
User::whereRaw('LOWER(name) = ?', [strtolower($request->name)])->get();

Escape Output to Prevent XSS

Use {{ }} for HTML escaping. Only use {!! !!} for trusted, pre-sanitized content.

Incorrect:

{!! $user->bio !!}

Correct:

{{ $user->bio }}

CSRF Protection

Include @csrf in all POST/PUT/DELETE Blade forms. Not needed in Inertia.

Incorrect:

<form method="POST" action="/posts">
    <input type="text" name="title">
</form>

Correct:

<form method="POST" action="/posts">
    @csrf
    <input type="text" name="title">
</form>

Rate Limit Auth and API Routes

Apply throttle middleware to authentication and API routes.

RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->ip());
});

Route::post('/login', LoginController::class)->middleware('throttle:login');

Validate File Uploads

Validate MIME type, extension, and size. Never trust client-provided filenames.

public function rules(): array
{
    return [
        'avatar' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
    ];
}

Store with generated filenames:

$path = $request->file('avatar')->store('avatars', 'public');

Keep Secrets Out of Code

Never commit .env. Access secrets via config() only.

Incorrect:

$key = env('API_KEY');

Correct:

// config/services.php
'api_key' => env('API_KEY'),

// In application code
$key = config('services.api_key');

Audit Dependencies

Run composer audit periodically to check for known vulnerabilities in dependencies. Automate this in CI to catch issues before deployment.

composer audit

Encrypt Sensitive Database Fields

Use encrypted cast for API keys/tokens and mark the attribute as hidden.

Incorrect:

class Integration extends Model
{
    protected function casts(): array
    {
        return [
            'api_key' => 'string',
        ];
    }
}

Correct:

class Integration extends Model
{
    protected $hidden = ['api_key', 'api_secret'];

    protected function casts(): array
    {
        return [
            'api_key' => 'encrypted',
            'api_secret' => 'encrypted',
        ];
    }
}