2026-08-04 21:40:49 +07:00
# Security Best Practices
## Mass Assignment Protection
Every model must define `$fillable` (whitelist) or `$guarded` (blacklist).
Incorrect:
```php
class User extends Model
{
protected $guarded = []; // All fields are mass assignable
}
```
Correct:
```php
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:
```php
public function update ( UpdatePostRequest $request , Post $post )
{
$post -> update ( $request -> validated ());
}
```
Correct:
```php
public function update ( UpdatePostRequest $request , Post $post )
{
Gate :: authorize ( 'update' , $post );
$post -> update ( $request -> validated ());
}
```
Or via Form Request:
```php
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:
```php
DB :: select ( "SELECT * FROM users WHERE name = ' { $request -> name } '" );
```
Correct:
```php
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:
```blade
{!! $user->bio !!}
```
Correct:
```blade
{{ $user->bio }}
```
## CSRF Protection
2026-08-17 23:24:31 +07:00
Include `@csrf` in all POST/PUT/DELETE Blade forms. Inertia doesn't use `@csrf` ; its HTTP client sends the `XSRF-TOKEN` cookie back as the `X-XSRF-TOKEN` header, which Laravel accepts in place of the `_token` field.
2026-08-04 21:40:49 +07:00
Incorrect:
```blade
<form method="POST" action="/posts">
<input type="text" name="title">
</form>
```
Correct:
```blade
<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.
```php
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 extension, MIME type, and size. The `mimes` rule checks extensions; use `mimetypes` for actual MIME type validation. Never trust client-provided filenames.
```php
public function rules () : array
{
return [
'avatar' => [ 'required' , 'image' , 'mimes:jpg,jpeg,png,webp' , 'max:2048' ],
];
}
```
Store with generated filenames:
```php
$path = $request -> file ( 'avatar' ) -> store ( 'avatars' , 'public' );
```
## Keep Secrets Out of Code
Never commit `.env` . Access secrets via `config()` only.
Incorrect:
```php
$key = env ( 'API_KEY' );
```
Correct:
```php
// 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.
```bash
composer audit
```
## Encrypt Sensitive Database Fields
Use `encrypted` cast for API keys/tokens and mark the attribute as `hidden` .
Incorrect:
```php
class Integration extends Model
{
protected function casts () : array
{
return [
'api_key' => 'string' ,
];
}
}
```
Correct:
```php
class Integration extends Model
{
protected $hidden = [ 'api_key' , 'api_secret' ];
protected function casts () : array
{
return [
'api_key' => 'encrypted' ,
'api_secret' => 'encrypted' ,
];
}
}
```