🔐 Gestion des mots de passe en PHP : Le guide sans bullshit

Publié le 13/01/2026 par Frédéric dans la catégorie "Php"

Le guide ultime pour maîtriser la gestion moderne des mots de passe en PHP : sécurisé, pratique et garanti sans MD5.

Prompt pour l'IA

Analyse ce code PHP de gestion d'authentification et agis comme un expert en cybersécurité (OWASP).

  1. Est-ce que les fonctions password_hash et password_verify sont correctement utilisées ?
  2. La base de données est-elle protégée contre les injections SQL (requêtes préparées) ?
  3. La session est-elle sécurisée après la connexion (régénération d'ID) ?
  4. Relève toute mauvaise pratique (utilisation de MD5, stockage de sel manuel, colonne BDD trop courte).

Explication détaillée

1️⃣ Pourquoi c’est critique ?

  • Stockage en clair : Si votre base de données est piratée, tous les mots de passe sont compromis. Or, les utilisateurs réutilisent souvent le même mot de passe ailleurs (banque, emails).
  • L'erreur MD5/SHA1 :
    • Trop rapides : Un attaquant peut tester des milliards de combinaisons par seconde.
    • Pas de sel (Salt) : Sans sel, on peut casser les mots de passe instantanément via des rainbow tables (annuaires de hashs pré-calculés).
  • La solution moderne :
    • password_hash() : Utilise bcrypt ou Argon2 (lent par conception) + génère un sel unique et automatique.

2️⃣ Les fonctions PHP à utiliser (et rien d’autre !)

Fonction Utilité Pourquoi l'utiliser ?
password_hash() Créer le hash Gère le sel et l'algorithme tout seul.
password_verify() Vérifier la connexion Protège contre les attaques temporelles.
password_needs_rehash() Mettre à jour la sécurité Permet de migrer vers un algo plus fort sans déconnecter l'utilisateur.

3️⃣ Exemple complet : De l'inscription à la connexion

📝 Étape 1 : Inscription

Avertissement BDD : Votre colonne password_hash doit absolument être un VARCHAR(255). Même si bcrypt fait 60 caractères, les futurs algorithmes (Argon2) sont plus longs. Ne tronquez pas vos hashs !

// Récupération du mot de passe
$password = $_POST['password'];

// Hachage avec l'algorithme par défaut (actuellement bcrypt)
$hash = password_hash($password, PASSWORD_DEFAULT);

// Insertion sécurisée avec requête préparée (obligatoire !)
$stmt = $pdo->prepare("INSERT INTO users (username, password_hash) VALUES (?, ?)");
$stmt->execute([$_POST['username'], $hash]);

🔑 Étape 2 : Connexion

On ne compare jamais un hash directement en SQL (WHERE password = $hash). On récupère le hash et on vérifie en PHP.

$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
$user = $stmt->fetch();

if ($user && password_verify($_POST['password'], $user['password_hash'])) {
    // Succès ! On sécurise la session
    session_start();
    session_regenerate_id(true); // Protection contre le vol de session
    $_SESSION['user_id'] = $user['id'];
    echo "Connexion réussie !";
} else {
    echo "Identifiants incorrects.";
}

4️⃣ Mise à jour automatique des hashs (Le mode Pro)

Si vous décidez de passer de Bcrypt à Argon2, ou d'augmenter la difficulté, utilisez password_needs_rehash() juste après password_verify() :

if (password_verify($password, $user['password_hash'])) {
    if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
        $newHash = password_hash($password, PASSWORD_DEFAULT);
        $update = $pdo->prepare("UPDATE users SET password_hash = ? WHERE id = ?");
        $update->execute([$newHash, $user['id']]);
    }
}

🚨 Erreurs courantes et comment les éviter

Erreur Conséquence Correction
Utiliser md5() Cassable en 1 seconde. Utiliser password_hash().
Stocker le sel à part Inutile et risqué. Laissez PHP l'inclure dans le hash final.
Colonne BDD trop courte Le hash est coupé, la connexion échoue. Utilisez VARCHAR(255).
Limiter à 15 caractères Affaiblit la sécurité. Laissez les utilisateurs mettre des phrases de passe longues.

💬 FAQ (Questions fréquentes)

1. "Pourquoi pas SHA-256 ?" SHA-256 est un algorithme rapide pour vérifier des fichiers. Pour les mots de passe, il nous faut un algorithme lent (comme Bcrypt) pour que la force brute devienne trop coûteuse pour le pirate.

2. "C'est quoi la limite des 72 caractères ?" Bcrypt ne lit que les 72 premiers caractères. Si un utilisateur met un mot de passe de 100 caractères, les 28 derniers seront ignorés. C'est rarement un problème, mais bon à savoir.

3. "Comment tester la puissance de mon serveur ?" Le paramètre cost (pour bcrypt) définit le temps de calcul. Voici un script pour trouver votre cost idéal (viser 0.05 à 0.1 seconde par hachage) :

$timeTarget = 0.05; // 50 millisecondes (compromis idéal vitesse/sécurité)
$cost = 8;
do {
    $cost++;
    $start = microtime(true);
    password_hash("test_password", PASSWORD_BCRYPT, ["cost" => $cost]);
    $end = microtime(true);
} while (($end - $start) < $timeTarget);

echo "Le coût optimal pour votre serveur est : " . $cost;

📚 Pour aller plus loin