| name | laravel-security |
| description | Laravel security patterns including Sanctum, Policies, Gates, and common vulnerability prevention. |
Laravel Security
Built-in Protection
Laravel protects automatically against:
| Attack | Protection | Your Responsibility |
|---|
| SQL Injection | Eloquent uses parameterized queries | Don't use raw queries with user input |
| XSS | Blade {{ }} escapes output | Don't use {!! !!} with user input |
| CSRF | @csrf directive | Include in all forms |
| Mass Assignment | $fillable/$guarded | Define on all models |
Authentication (Sanctum)
API Token Auth
$token = $user->createToken('api-token')->plainTextToken;
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn () => auth()->user());
});
SPA Auth (Cookie-based)
'supports_credentials' => true,
'stateful' => ['localhost', 'your-spa.test'],
Authorization (Policies)
Create Policy
php artisan make:policy PostPolicy --model=Post
Define Policy
class PostPolicy
{
public function view(User $user, Post $post): bool
{
return true;
}
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id
|| $user->is_admin;
}
}
Use Policy
public function update(Post $post)
{
$this->authorize('update', $post);
}
public function authorize(): bool
{
return $this->user()->can('update', $this->post);
}
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan
Gates
For non-model authorization:
Gate::define('access-admin', function (User $user) {
return $user->is_admin;
});
if (Gate::allows('access-admin')) {
}
@can('access-admin')
<a href="/admin">Admin Panel</a>
@endcan
Form Requests (Validation)
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string'],
'image' => ['nullable', 'image', 'max:2048'],
];
}
public function messages(): array
{
return [
'title.required' => 'A title is required.',
];
}
}
Rate Limiting
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
Route::middleware('throttle:login')->post('/login', LoginController::class);
Security Checklist
Authentication
Authorization
Input/Output
Configuration
Common Vulnerabilities to Avoid
DB::select("SELECT * FROM users WHERE id = $id");
User::find($id);
{!! $userInput !!}
{{ $userInput }}
User::create($request->all());
User::create($request->validated());