Le hashage PHP est un élément fondamental de la sécurité des applications web modernes. Lorsqu’un utilisateur crée un compte, vous n’êtes jamais censé stocker son mot de passe en clair, il faut le crypter. Le hashage permet de transformer ce mot de passe en une suite de caractères non réversible, de façon à ce que même en cas de fuite de données, les mots de passe d’origine restent protégés.
Dans ce tutoriel complet, vous apprendrez ce qu’est le hashage, à quoi il sert, ses avantages et limites, quand et quoi hasher, et surtout comment mettre en œuvre correctement le hashage des mots de passe en PHP avec des exemples concrets et des bonnes pratiques. Le but est que, à la fin, vous puissiez sécuriser vos formulaires d’inscription et de connexion de manière fiable, compréhensible et maintenable.
- Qu’est-ce que le hashage ?
- Avantages et limites du hashage
- Quand et quoi devez-vous hacher ?
- Principes de base pour hacher un mot de passe en PHP
- Exemple simple en PHP : inscription et connexion
- Exemple de réinitialisation sécurisée
- Cas d’usage avancés : hashage côté client ?
- Résumé technique rapide des commandes PHP utiles
- Exemple complet d’un petit module d’authentification
- Réglementation et conformité
Qu’est-ce que le hashage ?
Le hashage est une opération mathématique qui prend en entrée un message de taille quelconque et produit une sortie de taille fixe, appelée empreinte ou « digest ». Les fonctions de hashage cryptographique possèdent des propriétés importantes pour la sécurité : elles sont déterministes (même entrée = même sortie), rapides à calculer, résistantes aux collisions (il est difficile de trouver deux entrées différentes qui donnent la même sortie) et non réversibles (on ne peut pas retrouver l’entrée à partir de la sortie).
Dans le contexte des mots de passe, on utilise le hashage pour stocker une version transformée du mot de passe. Lorsqu’un utilisateur se connecte, on hashe le mot de passe proposé et on compare le résultat avec le hashé stocké. Si les deux correspondent, l’utilisateur est authentifié.
Hachage vs chiffrement : quelle différence ?
Le chiffrement (ou encryption) est une transformation réversible qui permet de récupérer le texte original en connaissant la clé de déchiffrement. Le hashage est irréversible : il n’existe pas de « clé » permettant de retrouver le mot de passe original à partir de son haché.
Vous chiffrez des données qui doivent être lues ultérieurement de façon sécurisée, tandis que vous hashez des secrets qu’il n’est pas nécessaire de retrouver, comme des mots de passe.
À quoi sert le hachage des mots de passe ?
Le hashage sert à réduire l’impact d’une fuite de base de données. Si un pirate accède à la table des utilisateurs, il n’obtient que des empreintes hachées, qui ne se transforment pas facilement en mots de passe exploitables.
Le hashage réduit aussi la responsabilité légale et la surface d’attaque. En outre, en combinant hashage et bonnes pratiques (paramètres de coût, vérification sûre, politique de mots de passe), vous augmentez significativement la sécurité offerte à vos utilisateurs.
Avantages et limites du hashage
Le principal avantage est la protection passive des mots de passe : même en cas de vol de données, les mots de passe originaux ne sont pas immédiatement disponibles.
Un autre avantage est la simplicité d’implémentation correcte en PHP grâce aux fonctions natives modernes.
Les limites sont réelles : le hachage n’empêche pas une attaque par force brute contre les empreintes si l’attaquant dispose d’une puissance de calcul élevée. Les méthodes classiques de hachage rapides (comme SHA-1) sont inadaptées pour les mots de passe, car elles permettent d’essayer des millions de mots de passe par seconde.
D’où l’importance d’utiliser des algorithmes lents et adaptés aux mots de passe (bcrypt, Argon2).
Quand et quoi devez-vous hacher ?
Vous devez hasher tout ce qui représente une authentification confidentielle et qui n’a pas besoin d’être récupéré à l’identique. Les mots de passe des comptes utilisateurs sont l’exemple principal. Ne hashez pas des informations qui doivent être lues à l’identique (numéros de carte bancaire pour paiements récurrents, informations légales nécessitant restitution). Pour ces dernières, utilisez un chiffrement sécurisé et une gestion de clés rigoureuse.
Les menaces que le hashage doit contrer
Un mécanisme de hashage correct doit protéger contre plusieurs attaques : le vol de base de données, les attaques par dictionnaire, les attaques par tables arc-en-ciel (rainbow tables), les attaques par force brute, et les attaques par GPU qui peuvent accélérer massivement le calcul de hachés.
Pour contrer ces attaques, on utilise des fonctions résistantes au calcul accéléré et des techniques complémentaires comme le salage, le pepper et des paramètres de coût.
Principes de base pour hacher un mot de passe en PHP
PHP propose depuis plusieurs versions des fonctions natives très simples d’utilisation et sûres : password_hash
, password_verify
et password_needs_rehash
. Elles encapsulent les bons choix de paramètres et évitent les erreurs courantes. Voici les principes à respecter :
- Utilisez
password_hash
pour créer le hashé. N’essayez pas d’utiliser SHA1, MD5, ou autre fonction de hachage « générale » pour les mots de passe. - Laissez
password_hash
générer le sel automatiquement. Vous n’avez pas besoin de stocker le sel séparément. - Stockez le haché tel quel dans votre base de données. Ne modifiez pas sa forme.
- Pour vérifier un mot de passe, utilisez
password_verify
. - Pour migrer vers un paramétrage plus sûr (coût supérieur, nouvel algorithme), utilisez
password_needs_rehash
et, au prochain login réussi, recalculer le haché et le mettre à jour. - Protégez l’accès à la table des utilisateurs : chiffrement des backups, accès restreint, logging, alerting.
- Appliquez des protections supplémentaires : limitation des tentatives, captchas, politique de mot de passe, 2FA.
Exemple simple en PHP : inscription et connexion
Voici un exemple minimaliste et expliqué montrant comment hasher un mot de passe en PHP au moment de l’inscription puis le vérifier pendant la connexion. Ce code doit être intégré dans un cadre sécurisé (requêtes préparées, HTTPS, filtrage), mais il illustre la mécanique.
// Inscription - création du compte
// Récupération des données (supposons $_POST['email'] et $_POST['password'])
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
// Validation basique des données (longueur, format email, etc.)
// ...
// Hachage du mot de passe avec password_hash()
// PASSWORD_DEFAULT utilise l'algorithme recommandé (actuellement bcrypt/Argon2 selon la version PHP)
$hash = password_hash($password, PASSWORD_DEFAULT);
// Exemple d'insertion sécurisée en base (PDO)
$pdo = new PDO('mysql:host=localhost;dbname=ma_db;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$stmt = $pdo->prepare('INSERT INTO users (email, password_hash, created_at) VALUES (:email, :hash, NOW())');
$stmt->execute([':email' => $email, ':hash' => $hash]);
// Connexion - vérification du mot de passe
// Récupération de l'email et du mot de passe saisi
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
// Récupération du haché stocké
$stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email = :email LIMIT 1');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password_hash'])) {
// Authentification réussie
// Optionnel : migrer le haché si besoin
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
$newHash = password_hash($password, PASSWORD_DEFAULT);
$upd = $pdo->prepare('UPDATE users SET password_hash = :hash WHERE id = :id');
$upd->execute([':hash' => $newHash, ':id' => $user['id']]);
}
// Démarrer la session, etc.
} else {
// Échec d'authentification
}
Explication détaillée : password_hash
prend le mot de passe en clair et retourne un texte encodé contenant l’algorithme, le coût et le sel. Vous stockez simplement cette chaîne. password_verify
hashe le mot de passe fourni en utilisant les mêmes paramètres encodés dans la chaîne et compare de façon sûre (résistance aux attaques temporelles).
Pour faire simple, la première étape est de hasher le mot de passe :
$hash = password_hash($password, PASSWORD_DEFAULT);
Quand un utilisateur se connecte, le mot de passe qu’il entre est d’abord haché. Ce nouveau hachage est ensuite comparé avec celui déjà enregistré dans la base de données. Si les deux correspondent, cela prouve que le mot de passe saisi est correct, sans jamais avoir besoin de stocker le mot de passe réel.
if (password_verify($password, $hash)) { /* mot de passe correct */ }
Choisir l’algorithme : bcrypt vs Argon2
PHP fournit deux familles d’algorithmes recommandés pour les mots de passe : bcrypt (via PASSWORD_BCRYPT) et Argon2 (via PASSWORD_ARGON2I et PASSWORD_ARGON2ID). PASSWORD_DEFAULT utilise l’algorithme recommandé par la plateforme au moment de l’exécution.
bcrypt est éprouvé depuis de nombreuses années et reste sûr si vous choisissez un coût adapté. Argon2, vainqueur du concours Password Hashing Competition en 2015, est actuellement considéré comme plus moderne : il offre une résistance améliorée aux attaques massivement parallèles grâce à des paramètres de mémoire et de temps.
Dans PHP, si vous utilisez password_hash
($pass, PASSWORD_DEFAULT), vous profiterez automatiquement de l’algorithme recommandé. Sur des installations récentes de PHP, cela peut être Argon2. Si vous souhaitez spécifier des paramètres, vous pouvez utiliser un tableau d’options.
Exemple Argon2id :
$options = [
'memory_cost' => 1<<16, // 65536 KiB = 64 MiB
'time_cost' => 4,
'threads' => 2
];
$hash = password_hash($password, PASSWORD_ARGON2ID, $options);
Exemple bcrypt avec coût :
$options = ['cost' => 12]; // coût (log2) ; 12 est un paramètre courant
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
Choisissez des paramètres qui équilibrent sécurité et performance. Testez sur votre serveur de production pour ne pas allonger excessivement le temps de traitement.
Salage, pepper, stretching : que faire et pourquoi
Le sel (salt) est une donnée aléatoire ajoutée au mot de passe avant hashage pour garantir que deux mêmes mots de passe n’auront pas le même hashé.
Les fonctions modernes comme password_hash
génèrent automatiquement un sel cryptographiquement sûr et l’intègrent dans la chaîne de hachage. Vous n’avez donc pas besoin de gérer le sel vous-même.
Le pepper est une valeur secrète, globale et non stockée dans la base de données, ajoutée au mot de passe avant hashage.
L’idée est que si la base de données est compromise, l’attaquant n’a pas accès au pepper. Le pepper doit être stocké séparément, idéalement dans une variable d’environnement sur le serveur ou dans un service de gestion de secrets.
Exemple :
$pepper = getenv('PASSWORD_PEPPER');
$hash = password_hash($password . $pepper, PASSWORD_DEFAULT);
Le stretching désigne l’utilisation d’un algorithme de hashage lent et/ou l’application répétée du hashage pour ralentir la force brute.

Des formations informatique pour tous !
Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…
Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.
Découvrez mes formations Qui suis-je ?Les algorithmes modernes (bcrypt, Argon2) intègrent ce principe via un paramètre de coût ou de temps et de mémoire. Vous n’avez généralement pas besoin d’implémenter manuellement le stretching si vous utilisez password_hash
avec un algorithme adapté.
Stockage en base de données : type et longueur
Stockez la chaîne retournée par password_hash
dans un champ texte. Utilisez VARCHAR(255) ou TEXT selon votre moteur de base de données. La longueur maximale de password_hash
varie selon l’algorithme, mais 255 caractères est suffisant dans la plupart des cas.
Exemple de schéma simplifié :
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Assurez-vous d’utiliser UTF-8 (utf8mb4) sur la colonne et la base de données pour éviter tout problème d’encodage.
Migration et mise à jour des paramètres de hashage
Les recommandations évoluent. Il est donc important d’implémenter une stratégie de migration. password_needs_rehash
permet de détecter si un hashé existant utilise des paramètres obsolètes. Lorsqu’un utilisateur se connecte avec succès, vérifiez si le hashé doit être recalculé. Si oui, calculez un nouveau hashé et mettez-la à jour en base.
Exemple :
if (password_verify($password, $user['password_hash'])) {
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT, $options)) {
$newHash = password_hash($password, PASSWORD_DEFAULT, $options);
// mise à jour en DB
}
}
Cette méthode permet une migration progressive sans forcer tous les utilisateurs à se reconnecter immédiatement.
Limiter les tentatives et prévenir les abus
Le hashage protège le stockage, mais il ne protège pas contre les attaques en ligne. Mettez en place des protections côté application pour limiter les tentatives de connexion : verrous après plusieurs échecs, délais exponentiels, captcha, enregistrement des IP, notifications d’activité suspecte. Ces mesures réduisent le risque d’attaques par force brute sur les comptes en ligne.
Pour en savoir plus sur le Pentesting, consultez nos guides sur Tester / Craquer un mot de passe et une attaque par BruteForceAI.
Bonnes pratiques pour la politique des mots de passe
Favorisez des contraintes raisonnables et un accompagnement utilisateur plutôt que des règles trop strictes qui poussent aux mots de passe faibles ou à la réutilisation.
Encouragez l’usage de gestionnaires de mots de passe et proposez l’authentification forte (2FA) en complément.
Lors d’un reset, n’envoyez jamais le mot de passe par email en clair ; fournissez plutôt un token de réinitialisation à usage unique, limité dans le temps.
Exemple de réinitialisation sécurisée
Le flux sécurisé de réinitialisation doit générer un token aléatoire stocké hashé en base, envoyé à l’utilisateur sous forme de lien, et expirant rapidement. À la réception du lien, l’utilisateur saisit son nouveau mot de passe, qui est haché puis stocké. N’envoyez jamais le nouveau mot de passe par email.
Exemple simplifié de création de token :
$random = random_bytes(32);
$token = bin2hex($random); // token envoyé par email
$tokenHash = password_hash($token, PASSWORD_DEFAULT); // stocker le haché
// stocker tokenHash dans password_resets (user_id, token_hash, expires_at)
Lors de la vérification, récupérez le haché depuis la table, utilisez password_verify pour vérifier le token reçu, puis supprimez l’entrée pour empêcher la réutilisation.
Pour en savoir plus, consultez notre guide sur la Création de token sécurisé en PHP.
Journalisation et chiffrement des backups
Même si les mots de passe sont hashés, vous devez protéger vos sauvegardes de base de données et vos exports. Chiffrez les fichiers de sauvegarde et limitez l’accès. Activez la journalisation des accès administratifs et surveillez les anomalies.
Attaques par timing et comparaisons sûres
Pour comparer des hashés ou des tokens, utilisez des fonctions résistantes aux attaques temporelles. password_verify
utilise une comparaison sécurisée. N’utilisez jamais d’opérateurs de comparaison simples pour comparer des hachés sensibles.
Erreurs courantes à éviter
- Ne jamais inventer votre propre algorithme de hashage.
- Ne pas utiliser MD5, SHA1, ou SHA256 seuls pour stocker des mots de passe.
- Ne pas stocker les mots de passe en clair.
- Ne pas loguer des mots de passe en clair ni les inclure dans les erreurs affichées.
- Ne pas transmettre de mots de passe par email.
- Ne pas oublier d’utiliser HTTPS sur les pages de connexion et d’inscription.
Pour en savoir plus sur la sécurisation SSL et HTTPS, consultez notre guide pour Obtenir un certificat Let’s Encrypt.
Mesurer et ajuster le coût
Testez le temps nécessaire pour calculer un hashé sur votre infrastructure. Un objectif raisonnable est une durée de quelques dizaines à quelques centaines de millisecondes par calcul de hashé au moment de l’authentification, mais cela dépend de vos contraintes d’échelle. Ajustez les paramètres de coût pour maintenir l’équilibre sécurité/performance.
Cas d’usage avancés : hashage côté client ?
Hasher côté client (dans le navigateur) n’est pas recommandé comme substitution au hashage côté serveur.
Vous pouvez utiliser une couche supplémentaire de protection client pour réduire la longueur du mot de passe en clair sur le réseau, mais cela ne remplace pas le hashage côté serveur. Le serveur doit toujours recevoir un secret et effectuer son propre hachage avec un sel sécurisé et des paramètres gérés côté serveur.
Perf et montée en charge
Le hashage sécurisé ajoute du coût CPU. Sur des sites à fort trafic, dimensionnez vos serveurs pour supporter les opérations de hashage (plusieurs milliers d’authentifications simultanées peuvent devenir coûteuses). Utilisez la mise en cache pour les opérations non sensibles, mais n’essayez pas de mettre en cache des hashés ou mots de passe. Pour réduire le pic de charges, répartissez les connexions sur plusieurs serveurs et optimisez le paramétrage du coût.
Résumé technique rapide des commandes PHP utiles
password_hash
crée un haché sécurisé en choisissant automatiquement un sel et en encodant les paramètres.password_verify
vérifie un mot de passe en toute sécurité.password_needs_rehash
détecte si la politique de hachage a changé et permet une migration progressive. Utilisez random_bytes pour générer des valeurs aléatoires cryptographiquement sûres (tokens, pepper si nécessaire).
Exemple complet d’un petit module d’authentification
Voici une implémentation simple et commentée, pour illustrer la logique complète d’inscription, connexion et migration du haché.
class Auth {
private $pdo;
private $pepper;
public function __construct(PDO $pdo, $pepper = '') {
$this->pdo = $pdo;
$this->pepper = $pepper;
}
public function register($email, $password) {
// validation...
$hash = password_hash($password . $this->pepper, PASSWORD_DEFAULT);
$stmt = $this->pdo->prepare('INSERT INTO users (email, password_hash) VALUES (:email, :hash)');
return $stmt->execute([':email' => $email, ':hash' => $hash]);
}
public function login($email, $password) {
$stmt = $this->pdo->prepare('SELECT id, password_hash FROM users WHERE email = :email LIMIT 1');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) return false;
if (password_verify($password . $this->pepper, $user['password_hash'])) {
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
$newHash = password_hash($password . $this->pepper, PASSWORD_DEFAULT);
$upd = $this->pdo->prepare('UPDATE users SET password_hash = :hash WHERE id = :id');
$upd->execute([':hash' => $newHash, ':id' => $user['id']]);
}
// démarrer session, etc.
return true;
}
return false;
}
}
Explication : le pepper est optionnel et apporte une sécurité supplémentaire en cas de fuite de la base de données, mais il doit être stocké séparément et en sécurité. Notez que si vous utilisez un pepper, vous devez l’ajouter automatiquement lors de la vérification et la génération du haché.
Réglementation et conformité
Selon la juridiction, les obligations peuvent varier. Toutefois, stocker les mots de passe hachés correctement améliore votre conformité avec les règles générales de protection des données. En Europe, le RGPD impose des mesures techniques et organisationnelles pour assurer la sécurité des données personnelles ; le hashage des mots de passe en fait partie.
Le hashage des mots de passe est une pratique incontournable pour toute application qui gère des comptes utilisateurs. En PHP moderne, il est devenu simple et sûr d’implémenter le hachage correctement grâce aux fonctions natives password_hash
, password_verify
et password_needs_rehash
.
Le plus important est de choisir un algorithme adapté (bcrypt ou Argon2), de gérer le coût de manière réfléchie, et d’appliquer des mesures complémentaires : limitation des tentatives, 2FA, chiffrement des sauvegardes, politique de mots de passe et gestion sécurisée des tokens de réinitialisation.
Ne cherchez pas la complexité inutile. Utilisez les primitives fournies par PHP, testez la charge sur votre infrastructure, et préparez une stratégie de migration pour les futurs algorithmes et paramètres.
Enfin, gardez à l’esprit que la sécurité est un ensemble ; le hashage protège le stockage des mots de passe, mais la sécurité globale dépend aussi de la protection des backups, des accès administratifs, et des pratiques de développement.