5. Questions de CyberSécurité⚓︎
Laravel est un framework très utilisé et donc sensible à une attaque Cyber. Il faut toujours rester en veille sur le framework et apporter les correctifs nécessaires dès que possible.
Ressources Documentaires :
- https://laravel.com/docs/12.x/authentication
- https://www.cybersecurity-help.cz/vdb/laravel/laravel_framework/12.0.0/
- https://www.cert.ssi.gouv.fr/avis/CERTFR-2025-AVI-0238/
1. Validation des entrées⚓︎
Toujours s’assurer que toutes les données saisies par l’utilisateur sont validées et assainies constitue la première ligne de défense contre de nombreuses attaques courantes, comme l’injection SQL ou le cross-site scripting (XSS). Laravel fournit de puissantes règles de validation que tu peux utiliser pour garantir l’intégrité des données.
$request->validate([
'username' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:8|confirmed',
]);
- Required → champ obligatoire
- string → doit être une chaîne de caractères
- max:255 → longueur maximale 255 caractères
- email → doit être une adresse e-mail valide
- unique:users,email → l’e-mail ne doit pas déjà exister dans la table users
- min:8 → minimum 8 caractères
- confirmed → doit correspondre au champ password_confirmation
Exemple déjà mis en place dans NewPasswordController.php :
public function store(Request $request): RedirectResponse
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
A faire
Ajouter une règle pour vérifier que le todo est une chaine obligatoire de caractères de moins de 256 car.
public function saveTodo(Request $request)
{
// Vérification des entrées du formulaire
$request->validate([
'texte' => 'required|string|max:256',
]);
....
}
2. Prevention contre SQL Injection⚓︎
L'utilisation de l'ORM Eloquent aide à prévenir les attaques par injection SQL. Utilisez toujours des requêtes paramétrées et évitez de concaténer l'entrée de l'utilisateur dans les instructions SQL.
// Safe query using Eloquent
$user = User::where('email', $email)->first();
3. Prévention des attaques XSS⚓︎
Pour éviter les attaques XSS, toujours échapper à l'entrée de l'utilisateur avant de la rendre dans la vue. Le moteur de modèle Blade de Laravel échappe automatiquement à la sortie des données en utilisant le {{ }} syntaxe.
<!-- Rendu sûr dans Blade -->
{{ $user->name }}
4. CSRF Protection⚓︎
Laravel inclut une protection intégrée de la CSRF. Assurez-vous que vos formulaires incluent le jeton CSRF en utilisant le @csrf Directive.
dans template.blade.php : <meta name="csrf-token" content="{{ csrf_token() }}">
et dans home.blade.php :
<form action="{{ route('todo.save') }}" method="POST" class="add">
@csrf
5. Limitation de débit et régulation des requêtes⚓︎
Pour protéger l'application contre les attaques par force brute, on peut mettre en place une limitation du nombre de requêtes grâce au middleware ThrottleRequests de Laravel.
dans routes/auth.php, repérer les routes /login et modifier les ainsi.
Route::get('login', [AuthenticatedSessionController::class, 'create'])
->middleware('throttle:10,1') // 10 requêtes / min sur l’affichage du formulaire
->name('login');
Route::post('login', [AuthenticatedSessionController::class, 'store'])
->middleware('throttle:5,1'); // 5 tentatives de connexion / min
- throttle:10,1 signifie : → maximum 10 requêtes par minute (le second chiffre indique la durée en minutes).
- Si la limite est dépassée, Laravel renvoie automatiquement une réponse 429 (Too Many Requests).
le throttling est utile pour protéger :
- l’accès aux endpoints sensibles (ex : login)
- les API exposées publiques
- les formulaires soumis à répétition
👀 Il faut tester !!
import requests
import time
URL = "http://localhost:8000/login" # adapte si besoin (port, domaine, etc.)
for i in range(1, 16): # 15 requêtes
response = requests.get(URL)
print(f"Requête {i}: status = {response.status_code}")
# Affiche quelques headers utiles si présents
print(" X-RateLimit-Limit :", response.headers.get("X-RateLimit-Limit"))
print(" X-RateLimit-Remaining :", response.headers.get("X-RateLimit-Remaining"))
print(" Retry-After :", response.headers.get("Retry-After"))
print("-" * 40)
# On attend très peu pour rester dans la même minute
time.sleep(1) # 2 secondes entre chaque requête
----------------------------------------
Requête 4: status = 200
X-RateLimit-Limit : 10
X-RateLimit-Remaining : 1
Retry-After : None
----------------------------------------
Requête 5: status = 200
X-RateLimit-Limit : 10
X-RateLimit-Remaining : 0
Retry-After : None
----------------------------------------
Requête 6: status = 429
X-RateLimit-Limit : 10
X-RateLimit-Remaining : 0
Retry-After : 8
----------------------------------------
💥 Ca sera bien de "rendre visible" le throttling dans les logs en interceptant l’exception 429 générée par le middleware throttle.
6. Journalisation et supervision : ThrottleRequestsException⚓︎
Lorsqu’un utilisateur dépasse la limite, Laravel lève une exception ThrottleRequestsException. Nous allons la capturer pour enregistrer un événement de sécurité.
🔧 Étape 1 : Modifier la gestion des exceptions dans bootstrap/app.php
Ouvre bootstrap/app.php.
Tu dois avoir quelque chose de ce type (structure Laravel 11/12) :
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
// ...
)
->withMiddleware(function (Middleware $middleware) {
// ...
})
->withExceptions(function (Exceptions $exceptions) {
// ...
})
->create();
Nous allons compléter le bloc withExceptions.
Ajoute les use nécessaires en haut du fichier :
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
Puis remplace / complète le bloc withExceptions ainsi :
->withExceptions(function (Exceptions $exceptions): void {
// Logging sécurité pour les dépassements de limite (HTTP 429)
$exceptions->render(function (ThrottleRequestsException $e, Request $request) {
Log::channel('security')->warning('Brute-force / rate limit déclenché', [
'ip' => $request->ip(),
'url' => $request->fullUrl(),
'method' => $request->method(),
'user_agent' => $request->userAgent(),
]);
// Ne rien retourner ici => Laravel utilisera la réponse 429 par défaut
// (page "Too Many Attempts" ou JSON selon le contexte).
});
// Tu peux laisser d’autres config ici si Laravel en a généré
})->create();
Render VS report
Dans Laravel 11/12, les exceptions peuvent être interceptées à deux niveaux distincts :
report() : pour décider si une exception doit être enregistrée dans les logs
report() sert à signaler une exception (log, Sentry, Bugsnag, etc.). Laravel ne reporte pas toutes les exceptions. Certaines exceptions sont explicitement ignorées (don’tReport).
Et certaines, comme le ThrottleRequestsException, ne sont pas considérées comme « erreurs applicatives » mais comme comportements normaux du middleware RateLimiter.
👉 Résultat : ThrottleRequestsException peut ne JAMAIS passer dans report() → donc aucun log, même si tu le demandes.
render() : pour intercepter toutes les exceptions juste avant la réponse HTTP
render() est appelé au moment où Laravel prépare la réponse HTTP :Quand une exception doit devenir un 429, 403, 404, 500, etc. Laravel appelle systématiquement render(), même si l’exception n’est pas "reportable".
Donc avec render(): On est certain que la logique se déclenche à chaque dépassement Throttle.
🔧 Étape 2 : Modifier config/logging.php
Création d’un canal de log dédié à la sécurité 📁🛡️. Pour isoler les traces d’attaque dans un fichier spécifique : storage/logs/security.log
Dans "channels", ajouter :
'security' => [
'driver' => 'single',
'path' => storage_path('logs/security.log'),
'level' => 'warning',
],
Analyse des logs de sécurité 📜🕵️♂️⚓︎
Ouvrir les logs storage/logs/security.log
Vous devez voir des entrées de ce type :
[2025-11-25 20:01:45] local.WARNING: Brute-force / rate limit déclenché {"ip":"127.0.0.1","url":"http://localhost:8000/login","method":"GET","user_agent":"python-requests/2.31.0"}
- IP de l’attaquant
- URL cible
- Méthode HTTP
- User-Agent du script automatisé
- Heure exacte de l’incident
Conclusion : Si tu veux tracer une attaque (brute force, flood HTTP, etc.), il faut intercepter l’exception au niveau où elle génère la réponse. 👉 C’est render(), pas report().
3.3.7 Journalisation des logins⚓︎
Pour tracer les connexions réussies et échouées, le plus simple est de le faire directement dans le contrôleur d’authentification (AuthenticatedSessionController) en réutilisant ton canal security.
Objectif :
Login OK → entrée “info” dans security.log
Login KO (mauvais mot de passe) → entrée “warning” dans security.log
Préparation : canal de log⚓︎
On réutilise le même canal que pour le throttle (déjà en place) dans config/logging.php :
'channels' => [ // ...
'security' => [
'driver' => 'single',
'path' => storage_path('logs/security.log'),
// Gestion du niveaux de logs dans le .env
'level' => env('LOG_LEVEL', 'debug'),
],
],
Journaliser dans AuthenticatedSessionController⚓︎
Ouvre app/Http/Controllers/Auth/AuthenticatedSessionController.php.
En haut du fichier, ajoute les use suivants :
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
Puis adapte la méthode store comme suit :
use Illuminate\Support\Facades\Log;
use App\Providers\RouteServiceProvider;
use Illuminate\Validation\ValidationException;
public function store(LoginRequest $request): RedirectResponse
{
try {
// Tentative d'authentification (throw ValidationException si KO)
$request->authenticate();
// Regénération de la session en cas de succès
$request->session()->regenerate();
// LOG: connexion réussie
Log::channel('security')->info('Login OK', [
'ip' => $request->ip(),
'email' => $request->input('email'),
'user_id' => optional($request->user())->id,
'user_agent' => $request->userAgent(),
]);
return redirect()->intended('/');
} catch (ValidationException $e) {
// LOG: tentative de connexion échouée
Log::channel('security')->warning('Login KO', [
'ip' => $request->ip(),
'email' => $request->input('email'),
'user_agent' => $request->userAgent(),
'errors' => $e->errors(), // optionnel, pour tracer le type d’erreur
]);
// On relance l’exception pour laisser Laravel gérer le retour formulaire + messages
throw $e;
}
}
**note : **
LoginRequest::authenticate()lève une ValidationException quand les identifiants sont faux.- Le try/catch permet d’instrumenter cette exception sans changer le comportement utilisateur.
- On trace IP, email, user_agent, éventuellement user_id si l’authentification a réussi.
👀 Il faut tester !!
Niveau de logs
Vous pouvez consigner des informations dans les journaux à l'aide de la façade Log. Comme mentionné précédemment, le système de journalisation propose huit niveaux de journalisation définis.
Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);