La session PHP est votre serrure — et la plupart des développeurs ne la changent jamais
Un attaquant qui obtient un identifiant de session valide n'a pas besoin de votre mot de passe. Il est, aux yeux de votre application, un utilisateur légitime. Contrairement à l'injection SQL ou au XSS qui ciblent le code, les attaques de session ciblent la couche d'identité — le mécanisme qui prouve qui est connecté après l'authentification. C'est pourquoi une mauvaise configuration des sessions peut annuler tous vos autres efforts de sécurité.
Cet article, inspiré d'une publication de Nchiminyi sur dev.to, détaille les vecteurs d'attaque les plus courants et les contre-mesures concrètes à mettre en place.
Comment fonctionne une session PHP (et pourquoi c'est un vecteur d'attaque)
PHP génère un identifiant de session unique (PHPSESSID) lors de l'appel à session_start(). Cet identifiant est transmis au navigateur via un cookie, et le serveur l'utilise pour retrouver les données de session stockées côté serveur (fichier, Redis, base de données).
Le problème fondamental : l'identifiant de session est un jeton d'accès. Qui le possède peut se faire passer pour l'utilisateur qu'il représente. Les attaquants le volent via plusieurs vecteurs :
- Session hijacking : interception du cookie sur un réseau non chiffré ou via XSS
- Session fixation : l'attaquant force un identifiant connu avant la connexion, puis attend que la victime s'authentifie avec cet ID
- Prédiction d'identifiant : si le générateur d'ID est faible ou prévisible, l'attaquant peut le deviner
L'erreur la plus fréquente : ne pas régénérer l'ID après connexion
La faille de session fixation est triviale à exploiter et à corriger. Elle repose sur un comportement simple : PHP conserve le même identifiant de session avant et après l'authentification.
Scénario d'attaque :
- L'attaquant visite votre site, obtient un
PHPSESSIDvalide - Il envoie un lien à la victime contenant cet identifiant forcé (via un paramètre GET ou un cookie injecté)
- La victime se connecte — son compte est maintenant associé à l'ID que l'attaquant connaît
- L'attaquant utilise cet ID pour accéder au compte
La correction est une ligne de code :
// Après validation des credentials, AVANT de stocker quoi que ce soit en session
session_regenerate_id(true); // true = supprime l'ancienne session
$_SESSION['user_id'] = $user->id;
$_SESSION['authenticated'] = true;
Le paramètre true est critique : il supprime le fichier de session précédent côté serveur, rendant l'ancien identifiant totalement invalide.
En Laravel, Auth::login() gère cette régénération automatiquement — mais si vous implémentez une authentification personnalisée, vérifiez que vous appelez $request->session()->regenerate() explicitement.
Configuration des cookies de session : les trois attributs indispensables
Même avec une régénération correcte, un cookie mal configuré reste vulnérable. Trois attributs sont non négociables en production.
HttpOnly — bloquer le vol par JavaScript
Sans cet attribut, n'importe quel script JavaScript peut lire document.cookie et exfiltrer votre session. Une faille XSS, même mineure, devient une compromission de compte.
// Dans php.ini ou en runtime
ini_set('session.cookie_httponly', 1);
// Ou via session_set_cookie_params()
session_set_cookie_params([
'httponly' => true,
'secure' => true,
'samesite' => 'Lax',
]);
Secure — forcer HTTPS
Sans cet attribut, le cookie de session peut être transmis sur une connexion HTTP non chiffrée. Un attaquant sur le même réseau (café, hôtel, réseau d'entreprise) peut l'intercepter avec un simple sniffer.
ini_set('session.cookie_secure', 1);
En environnement de développement local sans HTTPS, conditionnez cet attribut à l'environnement pour éviter de bloquer vos sessions en local.
SameSite — contrer le CSRF passif
L'attribut SameSite contrôle si le cookie est envoyé lors de requêtes cross-site. La valeur Lax (recommandée par défaut) bloque l'envoi du cookie dans la plupart des contextes cross-origin tout en autorisant la navigation directe.
// Configuration complète recommandée
session_set_cookie_params([
'lifetime' => 0, // Session expire à la fermeture du navigateur
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax', // ou 'Strict' pour les applications sensibles
]);
session_start();
En Symfony, ces paramètres se configurent dans framework.yaml :
framework:
session:
cookie_httponly: true
cookie_secure: auto # HTTPS en prod, HTTP en dev
cookie_samesite: lax
cookie_lifetime: 0
Aller plus loin : validation d'empreinte et expiration explicite
Même avec les attributs ci-dessus, un cookie volé reste exploitable jusqu'à son expiration. Deux pratiques complémentaires réduisent la fenêtre d'exploitation.
Lier la session à l'empreinte du client
Stockez en session un hash de l'User-Agent et d'une partie de l'IP lors de la connexion, et vérifiez-le à chaque requête :
function generateSessionFingerprint(Request $request): string
{
return hash('sha256',
$request->server->get('HTTP_USER_AGENT', '') .
substr($request->getClientIp() ?? '', 0, strrpos($request->getClientIp() ?? '', '.') + 1)
);
}
// À la connexion
$_SESSION['fingerprint'] = generateSessionFingerprint($request);
// À chaque requête protégée
if ($_SESSION['fingerprint'] !== generateSessionFingerprint($request)) {
session_destroy();
// Rediriger vers la page de connexion
}
⚠️ N'incluez jamais l'IP complète dans l'empreinte : les connexions mobiles changent d'IP fréquemment. Le
/24du réseau suffit.
Expiration côté serveur
Ne vous fiez pas uniquement à cookie_lifetime. Stockez le timestamp de dernière activité en session et invalidez explicitement les sessions inactives :
const SESSION_TIMEOUT = 1800; // 30 minutes
if (isset($_SESSION['last_activity'])) {
if (time() - $_SESSION['last_activity'] > SESSION_TIMEOUT) {
session_unset();
session_destroy();
// Rediriger
}
}
$_SESSION['last_activity'] = time();
Conclusion : une checklist en cinq points
La sécurité des sessions PHP repose sur des décisions de configuration, pas sur des algorithmes complexes. Voici les cinq points à vérifier sur chaque projet :
session_regenerate_id(true)appelé immédiatement après toute authentification réussie- Cookie
HttpOnlyactivé pour bloquer le vol via XSS - Cookie
Secureactivé en production pour forcer HTTPS - Cookie
SameSite: Lax(ouStrict) pour limiter les requêtes cross-site - Expiration côté serveur implémentée indépendamment de l'expiration du cookie
Ces cinq mesures ne remplacent pas une politique de sécurité globale, mais elles ferment les vecteurs d'attaque les plus exploités en pratique. Un attaquant avec un identifiant de session valide est votre utilisateur — votre travail est de rendre ce scénario aussi difficile que possible.
Source originale : Session Security in PHP — What Most Developers Get Wrong par Nchiminyi (dev.to)