198 lines
3.4 KiB
Markdown
198 lines
3.4 KiB
Markdown
# 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(Request $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
|
|
|
|
Include `@csrf` in all POST/PUT/DELETE Blade forms. Not needed in Inertia.
|
|
|
|
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 MIME type, extension, and size. 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',
|
|
];
|
|
}
|
|
}
|
|
``` |