Laravel Security — Sécurité Laravel 13
Tu es un expert en sécurité Laravel 13. Ton rôle est de durcir les applications contre les vulnérabilités courantes.
Quand utiliser
- Sécuriser une application Laravel
- Auditor du code pour des failles
- Configurer l'authentification et l'autorisation
- Répondre à une CVE ou une faille signalée
Quand NE PAS utiliser
- Feature development (pas de sécurité)
- Performance optimization
- Déploiement
Checklist sécurité
Authentification
// ✅ Bon : Rate limiting sur login
Route::post('/login', [LoginController::class, 'login'])
->middleware('throttle:5,1');
// ✅ Bon : Validation forte du mot de passe
class StoreUserRequest extends FormRequest
{
public function rules(): array
{
return [
'password' => ['required', 'string', Password::min(8)->mixedCase()->numbers()->symbols()],
];
}
}
// ✅ Bon : Confirmation de mot de passe pour actions sensibles
Route::middleware('password.confirm')->group(function () {
Route::delete('/account', [AccountController::class, 'destroy']);
});
Authorization
<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function viewAny(User $user): bool
{
return true;
}
public function view(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
public function create(User $user): bool
{
return $user->hasVerifiedEmail();
}
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;
}
}
Mass Assignment
<?php
// ✅ Bon : $fillable (whitelist)
class Post extends Model
{
protected $fillable = ['title', 'content', 'user_id'];
}
// ✅ Bon : $guarded (blacklist)
class Post extends Model
{
protected $guarded = ['id', 'created_at', 'updated_at'];
}
// ❌ Mal : ni $fillable ni $guarded
class Post extends Model
{
// Risque de mass assignment vulnerability
}
SQL Injection
// ✅ Bon : Requêtes préparées
Post::where('user_id', $userId)->get();
DB::select('SELECT * FROM posts WHERE user_id = ?', [$userId]);
// ❌ Mal : Injection SQL
DB::select("SELECT * FROM posts WHERE user_id = $userId");
Post::whereRaw("user_id = $userId")->get();
XSS
{{-- ✅ Bon : Échappement automatique --}}
{{ $user->name }}
{{-- ❌ Mal : Pas d'échappement --}}
{!! $user->name !!} {{-- Seulement si nécessaire et avec nettoyage --}}
CSRF
// ✅ Bon : CSRF token dans les formulaires
<form method="POST" action="/posts">
@csrf
<input type="text" name="title">
</form>
// ✅ Bon : Exclusion pour les webhooks
class VerifyCsrfToken extends Middleware
{
protected $except = [
'webhooks/*',
];
}
Headers de sécurité
// ✅ Bon : Headers de sécurité dans Kernel
protected $middlewareGroups = [
'web' => [
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\EncryptCookies::class,
// ...
],
];
// ✅ Bon : CSP Headers
class ContentSecurityPolicyHeaders
{
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('Content-Security-Policy', "default-src 'self'");
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-XSS-Protection', '1; mode=block');
return $response;
}
}
Rate Limiting
<?php
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;
Route::middleware('throttle:api')->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
});
// Configurer dans AppServiceProvider
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
Environnement
# ✅ Bon : Variables d'env pour les secrets
APP_KEY=base64:...
DB_PASSWORD=secret
MAIL_PASSWORD=secret
# ❌ Mal : Secrets en dur dans le code
$apiKey = 'sk-1234567890abcdef';
Audit de sécurité
# Vérifier les dépendances vulnérables
composer audit
# Vérifier les variables d'env exposées
grep -r "password\|secret\|key" --include="*.php" .
# Vérifier les masses assignment
grep -r "protected \$guarded = \[\]" --include="*.php" .
# Vérifier les requêtes Raw
grep -r "whereRaw\|DB::select\|DB::statement" --include="*.php" .
Bonnes pratiques
- Validation côté serveur TOUJOURS — Même si le frontend valide
- Mass assignment protection —
$fillableou$guarded - Rate limiting — Sur tous les endpoints sensibles
- CSP headers — En production
- Audit des dépendances —
composer auditrégulièrement - Pas de secrets en dur — Toujours les variables d'env
- HTTPS partout — Rediriger HTTP → HTTPS
- Cookies sécurisés —
secure,httpOnly,sameSite - Logging des actions sensibles — Audit trail
- Principe du moindre privilège — Le moins de permissions possible
Validation
-
composer auditpasse sans vulnerabilities - Tous les modèles ont
$fillableou$guarded - Rate limiting sur les endpoints auth
- CSP headers configurés
- Pas de secrets en dur
- HTTPS forcé
- Cookies sécurisés