Une base de données finit toujours par fuiter, par une faille applicative, une sauvegarde mal protégée ou un accès compromis. Ce qui distingue un incident gênant d'une catastrophe tient entièrement à la façon dont les mots de passe y étaient stockés. Le sujet est réglé depuis longtemps et n'appelle aucune créativité : PHP fournit une fonction dédiée qui applique un algorithme de hachage de mot de passe conçu pour être lent, salé automatiquement et paramétrable. Tout le travail consiste à l'utiliser correctement, à choisir un coût adapté au serveur, et à prévoir la migration des comptes existants lorsqu'on reprend une application ancienne. Le reste relève de mauvaises habitudes qu'il faut abandonner.
Pourquoi les anciennes méthodes ne conviennent plus
Comprendre ce qui rend une méthode inadaptée évite de la réintroduire par inadvertance, ce qui arrive plus souvent qu'on ne le croit sur les projets où le code ancien sert de modèle. Ce sujet fait partie du panorama que nous dressons à propos des failles de sécurité d'un site Internet.
Le stockage en clair
Conserver un mot de passe tel quel signifie que toute personne accédant à la base connaît immédiatement les identifiants de tous les comptes. Comme une large part des utilisateurs réemploie le même mot de passe ailleurs, la fuite dépasse largement le périmètre du site concerné et touche leurs comptes de messagerie, leurs boutiques et parfois leur banque. Cette pratique n'a plus aucune justification technique depuis vingt ans, et pourtant elle se rencontre encore sur des applications métier internes, où l'on suppose à tort que l'accès restreint suffit. Le signe qui ne trompe pas est un site capable de vous renvoyer votre mot de passe par courriel : il le connaît, donc il le stocke en clair ou de façon réversible.
Le chiffrement réversible
Chiffrer les mots de passe avec une clé paraît plus prudent et pose exactement le même problème : la clé se trouve quelque part sur le serveur, généralement dans le fichier de configuration que la même faille aura exposé. Le chiffrement est l'outil approprié pour des données que l'on doit pouvoir relire, un numéro de compte ou un document, comme nous l'évoquons à propos du standard AES-256-GCM. Un mot de passe n'a jamais besoin d'être relu : il suffit de pouvoir vérifier qu'une saisie correspond, ce qui est précisément ce que fait un hachage. Employer un chiffrement réversible ici traduit une confusion sur le besoin réel.
Les fonctions de hachage rapides
Les fonctions de hachage généralistes ont été conçues pour être rapides, qualité recherchée pour vérifier l'intégrité d'un fichier et défaut rédhibitoire pour protéger un mot de passe. Une carte graphique ordinaire calcule aujourd'hui plusieurs milliards de ces empreintes par seconde, ce qui permet de tester l'intégralité des mots de passe courants en quelques minutes. Le problème n'est pas que l'algorithme soit cassé, il est qu'il fait exactement ce pour quoi il a été conçu. Une empreinte produite par une fonction rapide, même sur un mot de passe complexe, ne protège que le temps que met un attaquant à lancer son outil.
Le sel, nécessaire mais pas suffisant
Ajouter une valeur aléatoire propre à chaque compte avant le hachage empêche l'emploi de tables précalculées et oblige à attaquer chaque mot de passe séparément. C'est indispensable et cela ne règle qu'une partie du problème : avec une fonction rapide, attaquer chaque compte séparément reste parfaitement faisable. Le sel doit par ailleurs être unique par compte et stocké avec l'empreinte, ce qui ne pose aucun problème puisqu'il n'a pas à rester secret. Les fonctions dédiées au mot de passe le génèrent et le stockent automatiquement, ce qui supprime définitivement cette question.
Ce qu'il faut à la place
Un algorithme conçu pour les mots de passe est délibérément lent et paramétrable, afin que son coût suive l'évolution du matériel. Il consomme du temps de calcul et, pour les plus récents, de la mémoire, ce qui rend les attaques massives sur carte graphique bien plus coûteuses. La lenteur est ici une fonctionnalité et non un défaut : quelques centaines de millisecondes par vérification sont imperceptibles pour un utilisateur qui se connecte, et rendent l'essai de milliards de combinaisons économiquement absurde. C'est tout le principe. Le paramètre de coût existe précisément pour que cette lenteur suive l'évolution du matériel sans qu'il faille changer d'algorithme.
Le mot de passe n'est qu'une partie
Un stockage impeccable ne protège pas contre un mot de passe deviné, contre le réemploi d'un identifiant volé ailleurs, ni contre l'absence de limitation des tentatives. Ces trois points relèvent d'autres mesures : politique de longueur minimale plutôt que de complexité artificielle, comparaison avec les listes de mots de passe connus comme compromis, limitation des essais et authentification à deux facteurs. Le hachage protège la base une fois qu'elle a fuité, ce qui est déjà considérable, et il ne remplace aucune de ces protections en amont. Les traiter ensemble, plutôt que de se contenter du hachage, est ce qui distingue une authentification correcte d'une authentification simplement conforme.
| Méthode | Résistance à une fuite | Verdict |
|---|---|---|
| Stockage en clair | Nulle | À corriger immédiatement |
| Chiffrement réversible | Très faible | Mauvais outil pour ce besoin |
| Hachage rapide sans sel | Très faible | Obsolète |
| Hachage rapide avec sel | Faible | Insuffisant |
| Hachage dédié à coût réglable | Bonne | Recommandé |
| Hachage dédié à coût mémoire | Très bonne | Préférable si disponible |

Utiliser la fonction du langage
PHP fournit deux fonctions qui suffisent à tout, l'une pour produire l'empreinte et l'autre pour vérifier une saisie. Elles règlent seules l'ensemble des questions de sel, de format de stockage et d'évolution d'algorithme.
Une seule ligne pour hacher
La fonction de hachage prend le mot de passe et une constante désignant l'algorithme, et retourne une chaîne contenant l'empreinte, le sel, l'identifiant de l'algorithme et les paramètres employés. Tout est dans cette chaîne, ce qui signifie qu'aucune colonne supplémentaire n'est nécessaire en base et qu'aucune information n'a besoin d'être conservée ailleurs. Ce format autodescriptif est la raison pour laquelle la migration d'un algorithme à un autre se fait sans difficulté, chaque empreinte portant en elle même les informations nécessaires à sa vérification.
Une seule ligne pour vérifier
La fonction de vérification prend la saisie et l'empreinte stockée, lit les paramètres dans cette dernière, recalcule et compare à temps constant. Il ne faut jamais rehacher soi même la saisie pour comparer les deux chaînes, opération qui échoue puisque le sel diffère à chaque appel, et qui expose en outre à une attaque par analyse temporelle si la comparaison est faite naïvement. Cette confusion est la première chose que l'on rencontre en reprenant du code écrit par quelqu'un qui n'avait pas lu la documentation. La fonction de vérification retourne un simple booléen, et son résultat doit être testé strictement plutôt que par une comparaison lâche.
Choisir l'algorithme
La constante par défaut désigne l'algorithme recommandé au moment de la version de PHP employée, et sa valeur évolue avec le temps. Employer cette constante plutôt qu'une valeur figée garantit de suivre les recommandations sans intervention, à condition d'avoir prévu la remise à jour des empreintes existantes. Les versions récentes proposent également un algorithme consommant de la mémoire, nettement plus résistant aux attaques sur matériel spécialisé, dont l'emploi est préférable lorsque le serveur le permet et que la mémoire disponible le tolère. Un serveur mutualisé aux ressources contraintes justifie en revanche de rester sur l'algorithme classique.
Régler le coût
Le paramètre de coût détermine le temps de calcul. La bonne méthode consiste à mesurer sur le serveur de production et à retenir la valeur qui donne entre cinquante et deux cent cinquante millisecondes par vérification. Une valeur trop basse affaiblit la protection, une valeur trop haute rend le site vulnérable à une saturation par tentatives de connexion répétées, chaque essai consommant du temps processeur. Ce réglage doit être refait lors d'un changement de serveur, un matériel plus rapide permettant d'augmenter le coût sans dégrader l'expérience.
Dimensionner la colonne
La chaîne produite fait actuellement soixante caractères pour l'algorithme le plus courant et davantage pour les autres. La documentation recommande de prévoir au moins deux cent cinquante cinq caractères, afin d'absorber les évolutions futures sans migration de schéma. Une colonne trop courte tronque silencieusement l'empreinte, ce qui empêche toute vérification ultérieure et produit des comptes définitivement inaccessibles, incident dont on ne se remet qu'en réinitialisant les mots de passe concernés.
Ne pas limiter la longueur
Il n'existe aucune raison technique de limiter la longueur d'un mot de passe à douze ou seize caractères, la fonction acceptant des chaînes bien plus longues et produisant toujours une empreinte de taille fixe. Ces limites, héritées de systèmes anciens, empêchent l'emploi de phrases de passe, qui constituent pourtant la meilleure pratique pour un utilisateur ordinaire. Une limite haute reste utile pour éviter qu'un envoi de plusieurs mégaoctets ne consomme du temps de calcul, et quelques centaines de caractères suffisent largement.
Migrer une application existante
La reprise d'un site stockant les mots de passe autrement demande une méthode, car on ne connaît pas les mots de passe en clair et l'on ne peut donc pas simplement recalculer les empreintes.
La migration à la connexion
La méthode de référence consiste à conserver l'ancien mécanisme pour la vérification, et à recalculer une empreinte moderne au moment où l'utilisateur se connecte avec succès, puisque c'est le seul instant où le mot de passe en clair est disponible. Le code teste d'abord le nouveau format, puis l'ancien en repli, et remplace l'empreinte lorsqu'il a validé par l'ancienne voie. Cette bascule est transparente pour l'utilisateur et migre la base progressivement, au rythme des connexions réelles, sans jamais interrompre le service.
Le double hachage transitoire
Lorsqu'on veut se débarrasser immédiatement des anciennes empreintes sans attendre les connexions, on peut appliquer le hachage moderne sur l'ancienne empreinte plutôt que sur le mot de passe. La vérification consiste alors à appliquer l'ancien algorithme à la saisie puis à vérifier le résultat. Cette technique protège la base tout de suite, au prix d'une complexité supplémentaire dans le code de vérification, et elle se combine avec une migration progressive vers le format simple lors des connexions suivantes.
La fonction de remise à niveau
Une fonction dédiée indique si une empreinte a été produite avec des paramètres différents de ceux en vigueur. Son appel après chaque connexion réussie, suivi d'un recalcul lorsque la réponse est positive, maintient la base à jour lorsqu'on augmente le coût ou que l'on change d'algorithme. C'est un mécanisme de trois lignes qui rend le dispositif durable, et son absence explique les bases où coexistent des empreintes de cinq générations différentes.
Les comptes inactifs
Les comptes dont les titulaires ne se connectent plus ne seront jamais migrés par la méthode progressive. Au bout d'un délai raisonnable, six à douze mois, il faut décider de leur sort : soit forcer une réinitialisation à la prochaine connexion, soit supprimer les comptes réellement abandonnés. Laisser indéfiniment des empreintes obsolètes annule une partie du bénéfice de la migration, puisque ce sont précisément ces comptes que la fuite exposerait le plus facilement.
Ne pas forcer une réinitialisation générale
Demander à tous les utilisateurs de changer leur mot de passe paraît radical et efficace, et produit en pratique un taux d'abandon élevé, des sollicitations du support et une perte de comptes. Cette mesure ne se justifie qu'en cas de compromission avérée. La migration progressive obtient le même résultat sans aucune friction, ce qui en fait le choix par défaut dans presque toutes les situations de reprise.
Journaliser la progression
Un comptage régulier du nombre d'empreintes restant dans l'ancien format donne l'avancement de la migration et permet de décider quand couper le mécanisme de repli. Sans ce suivi, l'ancien code reste indéfiniment en place par précaution, ce qui maintient une complexité et un risque inutiles. Une requête de comptage suffit, et elle se lit en une seconde sur n'importe quelle base.
Répartition constatée sur des applications reprises en maintenance. Près d'un tiers des cas relève encore d'un mécanisme obsolète depuis plus de dix ans.
Vérifier et compléter
Quelques contrôles simples confirment que le dispositif fonctionne, et quelques mesures complémentaires en tirent le meilleur parti.
Regarder ce qu'il y a en base
Le contrôle le plus direct consiste à ouvrir la table des utilisateurs et à examiner la colonne concernée. Une empreinte moderne commence par un identifiant d'algorithme reconnaissable et fait au moins soixante caractères. Des chaînes de trente deux caractères hexadécimaux signalent une fonction rapide, et des chaînes lisibles signalent un stockage en clair. Ce contrôle prend dix secondes et devrait être le premier réflexe lors de la reprise de toute application.
Mesurer le temps de vérification
Un chronométrage de la fonction de vérification sur le serveur de production confirme que le coût retenu produit bien le délai visé. Un temps très inférieur à cinquante millisecondes signale un coût trop faible, souvent parce qu'un réglage a été copié depuis un environnement de développement plus lent. Cette mesure prend deux minutes et doit être refaite après chaque changement d'hébergement ou de version du langage.
Limiter les tentatives
Le coût de calcul protège la base après une fuite, il ne protège pas contre les essais répétés sur le formulaire de connexion. Une limitation par compte et par adresse, avec un délai croissant, est indispensable et se met en place en quelques lignes. Elle protège en outre le serveur d'une saturation, chaque vérification consommant délibérément du temps processeur, ce qui fait du formulaire de connexion une cible commode.
Sécuriser la réinitialisation
Le mécanisme de réinitialisation est souvent le maillon faible : jeton trop court, sans expiration, réutilisable, ou transmis dans une adresse conservée dans les journaux. Le jeton doit être imprévisible, à usage unique, valide moins d'une heure, et son empreinte seule doit être stockée. Cette exigence est la même que pour les jetons de session, sujet traité dans notre article sur les sessions PHP et leur configuration.
Comparer aux listes de compromission
Vérifier au moment de l'inscription que le mot de passe choisi ne figure pas parmi ceux connus comme compromis apporte davantage de sécurité réelle que toutes les règles de complexité. Ce contrôle se fait sans transmettre le mot de passe, par un mécanisme de comparaison partielle proposé par les services dédiés. Il est bien accepté par les utilisateurs, à condition d'être expliqué clairement plutôt que présenté comme un refus arbitraire.
Abandonner les règles de complexité
Imposer une majuscule, un chiffre et un caractère spécial produit des mots de passe courts et prévisibles, que les utilisateurs notent quelque part. Les recommandations actuelles privilégient une longueur minimale généreuse, l'absence d'expiration périodique et la comparaison aux listes de compromission. Ce changement de doctrine est bien documenté et il reste peu appliqué, alors qu'il améliore simultanément la sécurité et le confort, situation suffisamment rare pour être signalée. Elle mérite d'être expliquée aux équipes, la règle des trois types de caractères étant profondément ancrée dans les habitudes. L'explication gagne à s'appuyer sur un exemple chiffré comparant deux mots de passe, l'un long et simple, l'autre court et complexe, démonstration bien plus convaincante qu'un rappel de la règle en vigueur.
💬 0 commentaires
✍️ Laisser un commentaire
Créez un compte gratuit ou connectez-vous pour commenter.