Durcir l’authentification par session
Objectif : Protéger la connexion par mot de passe et renouveler l’identifiant de session après authentification.
Une session qui conserve le même identifiant avant et après la connexion peut laisser un attaquant réutiliser un identifiant qu’il connaissait déjà. Un mot de passe comparé directement à une valeur en clair expose aussi un secret durable en cas de fuite de configuration ou de base de données. Vous allez vérifier un mot de passe haché, renouveler l’identifiant au bon moment et encadrer le cycle de vie de la session.
Hacher le mot de passe de référence
Ne conservez jamais le mot de passe du carnet en clair. PHP fournit password_hash() pour produire une empreinte adaptée au stockage ; l’algorithme et son sel sont gérés par la fonction. Générez l’empreinte une fois, puis placez-la dans une configuration protégée, par exemple une variable d’environnement, plutôt que dans un fichier servi publiquement.
<?php
declare(strict_types=1);
$motDePasse = 'remplacez-par-un-secret-long-et-unique';
$empreinte = password_hash($motDePasse, PASSWORD_DEFAULT);
if ($empreinte === false) {
throw new RuntimeException('Impossible de générer l’empreinte.');
}
fwrite(STDOUT, $empreinte . PHP_EOL);Une empreinte de mot de passe, différente à chaque génération. Elle peut être conservée comme valeur de configuration ; ne conservez pas le mot de passe de démonstration.La valeur produite n’est pas destinée à être déchiffrée. À la connexion, password_verify() compare le mot de passe soumis à cette empreinte et gère les paramètres nécessaires à la vérification. L’authentification ne doit pas renouveler la session si cette vérification échoue.
Encadrer la session
La classe SessionSecurisee concentre le démarrage, le renouvellement de l’identifiant et la destruction de la session. Configurez les options avant session_start() : le mode strict réduit l’acceptation d’identifiants imposés, tandis que les attributs du cookie limitent son exposition. En production, servez le site en HTTPS afin que l’option secure soit activée.
<?php
declare(strict_types=1);
final class SessionSecurisee
{
public function demarrer(): void
{
if (session_status() === PHP_SESSION_ACTIVE) {
return;
}
ini_set('session.use_strict_mode', '1');
ini_set('session.use_only_cookies', '1');
$https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => $https,
'httponly' => true,
'samesite' => 'Lax',
]);
if (!session_start()) {
throw new RuntimeException('Impossible de démarrer la session.');
}
}
public function renouvelerIdentifiant(): void
{
$this->demarrer();
if (!session_regenerate_id(true)) {
throw new RuntimeException('Impossible de renouveler la session.');
}
}
public function marquerAuthentifie(): void
{
$this->demarrer();
$_SESSION['authentifie'] = true;
}
public function estAuthentifie(): bool
{
$this->demarrer();
return ($_SESSION['authentifie'] ?? false) === true;
}
public function detruire(): void
{
if (session_status() !== PHP_SESSION_ACTIVE) {
return;
}
$_SESSION = [];
$parametres = session_get_cookie_params();
setcookie(session_name(), '', [
'expires' => time() - 42000,
'path' => $parametres['path'],
'domain' => $parametres['domain'],
'secure' => $parametres['secure'],
'httponly' => $parametres['httponly'],
'samesite' => $parametres['samesite'],
]);
session_destroy();
}
}Vérifier puis authentifier
AuthentificationService reçoit l’empreinte depuis la configuration et la session par injection. Il ne produit aucune sortie HTTP : il retourne le résultat de la connexion, que le contrôleur pourra traduire en réponse. Ce découpage garde la vérification indépendante de la présentation, conformément aux responsabilités des couches déjà étudiées.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../Securite/SessionSecurisee.php';
final class AuthentificationService
{
public function __construct(
private readonly string $empreinteMotDePasse,
private readonly SessionSecurisee $session
) {
}
public function connecter(string $motDePasse): bool
{
$this->session->demarrer();
if (!password_verify($motDePasse, $this->empreinteMotDePasse)) {
return false;
}
$this->session->renouvelerIdentifiant();
$this->session->marquerAuthentifie();
return true;
}
public function estAuthentifie(): bool
{
return $this->session->estAuthentifie();
}
public function deconnecter(): void
{
$this->session->detruire();
}
}Le point de composition transmet l’empreinte configurée à ce service et lui fournit une instance de SessionSecurisee. Après un échec, connecter() renvoie false et ne pose aucun marqueur d’authentification ; après un succès, le nouvel identifiant est créé avant l’enregistrement de l’état connecté. Lors de la déconnexion, appelez deconnecter() : les données en mémoire sont vidées, le cookie est expiré et les données serveur sont détruites.
À retenir
- Stockez une empreinte produite par password_hash(), jamais le mot de passe en clair.
- Vérifiez le secret avec password_verify() avant toute authentification.
- Renouvelez l’identifiant de session après la vérification réussie et avant de marquer la session comme connectée.
- Configurez les cookies HttpOnly et SameSite, activez Secure sous HTTPS et détruisez les données de session à la déconnexion.
Vous pourrez ensuite mesurer une lenteur reproductible sur la liste et la recherche avant d’examiner les requêtes SQL concernées.