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',
];
}
}