oujood.com

Comment hacher les mots de passe en PHP avec la fonction password_hash

Tutoriel complet, étape par étape comment hacher les mots de passe en PHP? : Vous apprendrez pourquoi les hachages comme MD5 ne sont pas sécurisés.
Comment créer des hachages de mot de passe sécurisés avec password_hash() Comment vérifier les mots de passe avec password_verify() deux façons d'augmenter la sécurité des hachages Tutoriel BONUS : comment convertir automatiquement les anciens hashs ? Donc, si vous voulez apprendre à crypter des mots de passe en PHP, ce tutoriel est fait pour vous

Sécurité du cryptage des mots de passe en PHP

Un développeur PHP doit savoir comment stocker les mots de passe de manière sécurisée.

Pour les raisons suivantes :

Un grand nombre d'attaques web visent le vol des mots de passe des utilisateurs.

En cas de vol de mots de passe, vous devez vous assurer que l'attaquant ne peut pas les décrypter.

La réglementation stricte d'aujourd'hui dans plusieurs payés en matière de confidentialité exige que les données sensibles, comme les mots de passe, soient protégées. Le non-respect de ces réglementations peut entraîner des sanctions.

La sécurité des mots de passe est l'une des caractéristiques de base de la sécurité PHP que vos clients attendent de vous.

Si vous apprenez à le faire correctement, votre réputation s'améliorera et vos clients vous feront confiance.

Commençons donc par cette question :

Les hachages MD5 et SHA sont-ils sûrs ?

(Réponse courte : non)

À l'époque, les mots de passe étaient stockés en utilisant un hachage MD5 ou SHA1.

Comme ceci :

      Copier le code

<? php
/* Mot de passe de l'utilisateur. */
$password = 'mon mot de passe';

/* Hachage MD5 à enregistrer dans la base de données. */
$hash = md5($password);
?>
Cependant, cette technique n'est pas assez sûre.

Pour deux raisons :

1- Les algorithmes MD5 et SHA sont trop faibles pour la puissance de calcul actuelle.
2- Les hachages simples, not-salted, sont vulnérables aux "tables rainbow" et aux attaques par dictionnaire.

Si un attaquant vole un hachage MD5 ou SHA, il peut aussi facilement trouver le mot de passe original.

En d'autres termes, ces hachages sont presque aussi peu sûrs que les mots de passe en texte clair.

La solution consiste à utiliser une fonction de hachage sécurisée : password_hash().

Comment fonctionne la fonction password_hash()

La fonction password_hash() crée un hachage sécurisé de votre mot de passe.

Voici comment utiliser cette fonction :

      Copier le code

<? php
/* Mot de passe de l'utilisateur. */
$password = 'mon mot de passe';
/* Hachage sécurisé du mot de passe. */
$hash = password_hash($password, PASSWORD_DEFAULT);
?>
Le résultat du hachage de password_hash() est sécurisé pour les raisons suivantes :

Il utilise un algorithme de hachage fort.
Il ajoute un salt aléatoire pour éviter les tables fantômes(rainbow) et les attaques par dictionnaire.

Une fois que vous avez le hash du mot de passe, vous pouvez l'enregistrer directement dans la base de données.

Voici comment procéder dans ce qui suit.

Comment utiliser la fonction password_hash()

Tout d'abord, vous avez besoin d'une table d'utilisateurs dans une base de données. Par exemple, utilisons une version simplifiée de la table "membres" du tutoriel sur Comment faire un espace membre en php. Cette table a les colonnes suivantes : - un champ id AUTO_INCREMENT permettant d'identifier chaque membre (chaque membre ayant un id différent). Ce sera notre clé primaire. - un champ login de type text qui contiendra le login de chaque membre de l'espace membre - un champ passwd de type text contenant le mot de passe haché de chaque membre. Voici le code SQL pour créer la table (vous pouvez l'utiliser avec PhpMyAdmin pour créer la table dans votre environnement de développement) :

      Copier le code

CREATE TABLE `membres` (
  `id` int(10) UNSIGNED NOT NULL,
  `login` varchar(255) NOT NULL,
  `passwd` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `membres`
  ADD PRIMARY KEY (`id`);
Important : Assurez-vous de définir la colonne du mot de passe en tant que varchar.
(Un varchar est une colonne de texte de taille variable).

La raison en est que la taille du hachage provenant de password_hash() peut changer (plus de détails à ce sujet plus tard).

Maintenant, vous devez vous connecter à la base de données depuis votre script PHP.

Si vous avez besoin d'aide avec php et MySQL, vous pouvez trouver tout ce dont vous avez besoin ici : Comment utiliser PHP avec MySQL

Voici un script de connexion PDO simple que vous pouvez utiliser immédiatement.

Modifiez simplement les paramètres de connexion pour qu'il fonctionne avec votre propre environnement :

Fichier pdo.php          Copier le code

 
/* Nom d'hôte du serveur MySQL. */
$host = 'localhost' ;

/* Nom d'utilisateur du compte MySQL. */
$user = `Utilisateur' ;

/* Mot de passe du compte MySQL. */
$passwd = 'monPasswd' ;

/* Le schéma par défaut que vous souhaitez utiliser. */
$schema = 'monSchema' ;
/* L'objet PDO. */
$pdo = NULL ;
/* Chaîne de connexion, ou "nom de la source de données". */
$dsn = 'monsql:host=' . $host . ';dbname=' . $schema ;

/* Connexion à l'intérieur d'un bloc try/catch. */
try
{  
   /* Création de l'objet PDO. */
   $pdo = new PDO($dsn, $user, $passwd) ;
   
   /* Activation des exceptions sur les erreurs. */
   $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) ;
}
catch (PDOException $e)
{
   /* S'il y a une erreur, une exception est levée. */
   echo 'La connexion à la base de données a échoué.' ;
   die() ;
}
Vous êtes maintenant prêt à ajouter un nouvel utilisateur à la table.

Voici un exemple complet (pdo.php est le script contenant le code de connexion à la base de données mentionné précédemment) :

Fichier ajout_membre.php          Copier le code

<? php
/* Inclure le script de connexion à la base de données. */
include 'pdo.php' ;

/* Nom d'utilisateur. */
$username = 'John' ;

/* Mot de passe. */
$password = 'my secret password' ;

/* Hachage sécurisé du mot de passe. */
$hash = password_hash($password, PASSWORD_DEFAULT) ;

/* Insertion d'un modèle de requête. */
$query = 'INSERT INTO membres (login, passwd) VALUES (:login, :passwd)' ;

/* Tableau de valeurs pour PDO. */
$values = [':login' => $username, ':passwd' => $hash] ;

/* Exécution de la requête. */ 
try
{
  $res = $pdo->prepare($query) ;
$res->execute($values) ;
}
catch (PDOException $e)
{
  /* Erreur de requête. */
  echo 'Erreur de requête.' ;
  die() ;
}?>

Important : Dans cet exemple, nous avons sauté les étapes de validation, dont :

La vérification de la longueur du nom d'utilisateur et du mot de passe.
La vérification des caractères non autorisés.
La vérification de l'existence du nom d'utilisateur.Et ainsi de suite. ...

La validation n'entre pas dans le cadre de ce tutoriel, mais n'oubliez pas que vous devez toujours valider vos variables d'entrée.
Vous pouvez vous référer à notre tutoriel sur Comment faire un espace membre en php pour plus de détails.

Comment vérifier le mot de passe? : utilisation de la fonction password_verify() de php

Pour vérifier le mot de passe fourni par un utilisateur distant, vous devez utiliser la fonction password_verify().

password_verify() prend deux arguments :

Le mot de passe que vous devez vérifier, comme premier argument
Le résultat de password_hash() du mot de passe original, comme second argument.

Si le mot de passe est correct, password_verify() renvoie true.

Voici un exemple :

          Copier le code

/* Inclure le script de connexion à la base de données. */
inclure 'pdo.php' ;
/* Statut de connexion : false = non authentifié, true = authentifié. */
$login = FALSE ;
/* Nom d'utilisateur du formulaire de connexion. */
$username = $_POST['username'] ;
/* Mot de passe du formulaire de connexion. */
$password = $_POST['password'] ;
/* N'oubliez pas de valider $username et $password. */
/* Recherche du nom d'utilisateur dans la base de données. */
$query = 'SELECT * FROM membres WHERE (login = :nom)' ;
/* Tableau de valeurs pour PDO. */
$values = [':nom' => $username] ;
/* Exécution de la requête */
try
{
  $res = $pdo->prepare($query) ;
  $res->execute($values) ;
}
catch (PDOException $e)
{
  /* Erreur de requête. */
echo `Query error.' ;
  die() ;
}
$row = $res->fetch(PDO::FETCH_ASSOC) ;
/* S'il y a un résultat, vérifiez si le mot de passe correspond en utilisant password_verify(). */
if (is_array($row))
{
  if (password_verify($password, $row['passwd']))
  {
    /* Le mot de passe est correct. */
    $login = TRUE ;
  }
}

Important :Comme dans l'exemple précédent, nous avons sauté les étapes de validation., ne les oublier surtout pas

Important : Vous ne pouvez pas simplement comparer deux hachages différents pour voir s'ils correspondent.

La raison en est que password_hash() crée des hachages saltés(protégé).
Les hachages saltés comprennent une chaîne aléatoire, appelée "salt", comme protection contre les tables fantômes(rainbow) et les attaques par dictionnaire.
Par conséquent, chaque hachage sera différent même si le mot de passe source est le même.

Essayez le code suivant. Vous verrez que les deux hachages sont différents, même si le mot de passe est le même :

          Copier le code

$password = 'mon password';
echo password_hash($password, PASSWORD_DEFAULT);
echo '
'; echo password_hash($password, PASSWORD_DEFAULT);

Note : La fonction password_verify() ne fonctionne qu'avec les hachages créés par la fonction password_hash().


Vous ne pouvez pas utiliser la fonction password_verify() pour vérifier un mot de passe sur la base d'un hachage MD5 ou SHA.

Comment augmenter la sécurité des hachages

La fonction password_hash() utilise l'algorithme de hachage par défaut Bcrypt.
Cet algorithme prend un paramètre optionnel nommé "cost". La valeur par défaut du cost est 10.
Le hachage généré par password_hash() est très sécurisé.

Mais vous pouvez le rendre encore plus solide grâce à deux techniques simples :

Augmenter le cost de Bcrypt.
Mettre à jour automatiquement l'algorithme de hachage.

En augmentant le cost, vous pouvez rendre le hash plus difficile à calculer. Plus le cost est élevé, plus le temps nécessaire pour créer le hash est long.

Plus le coût est élevé, plus il est difficile de casser le code de hachage. Cependant, la création et la vérification du hachage sont également plus longues.

Vous devez donc trouver un compromis entre la sécurité et la charge du serveur.

C'est ainsi que vous pouvez définir une valeur du cost personnalisée pour la fonction password_hash() :

          Copier le code

/* Password. */
$password = 'monpassword secret ';
/* mettre le "cost" à 12. */
$options = ['cost' => 12];
/* Créer le hash. */
$hash = password_hash($password, PASSWORD_DEFAULT, $options);

Mais quelle valeur de coût devez-vous définir ?

Un bon compromis est une valeur de coût qui permet à votre serveur de créer le hachage en 100 ms environ.

Voici un test simple pour trouver cette valeur :

          Copier le code

<?php
/* un time ge 100 ms. */
$time = 0.1;
/* Le cost initial . */
$cost = 10;
/* /* Boucle jusqu'à ce que le temps requis soit supérieur à 100ms. */ 
do
{
  $cost++;
  
  /*  Vérifier le temps nécessaire à la création du hachage. */
  $start = microtime(true);
  password_hash('test', PASSWORD_BCRYPT, ['cost' => $cost]);
  $end = microtime(true);
}
while (($end - $start) < $time);
echo 'Le cost trouvé est : '. $cost;
?>
Une fois que vous avez trouvé votre coût, vous pouvez l'utiliser chaque fois que vous exécutez password_hash() comme dans l'exemple précédent.
Moi j'ai trouvé 12

Comment changer ou réinitialiser le mot de passe d'un utilisateur

Si l’utilisateur décide de modifier son mot de passe ou qu’il l’a oublier et il faut le réinitialiser.
L'exemple suivant montre comment changer ou réinitialiser le mot de passe d'un utilisateur existant.

Commencez par obtenir le nouveau mot de passe via un formulaire dédier à cet effet et créez son hachage avec password_hash() :
pour plus de détail à ce sujet voir notre tutoriel Manipulation des Formulaires en PHP

          Copier le code

/* Nouveau mot de passe. */
$password = $_POST['password'] ;
/* N'oubliez pas de valider le mot de passe. */
/* Créez le hachage du nouveau mot de passe. */
$hash = password_hash($password, PASSWORD_DEFAULT) ;
Ensuite, mettez à jour l'enregistrement de la table de base de données ayant le même ID de compte que l'utilisateur actuel et définissez le nouveau hash.

Note : nous supposons que la variable $Id contient l'ID du compte.

          Copier le code

/* Inclure le script de connexion à la base de données. */
include 'pdo.php' ;
/* ID du compte que l'on veut modifier. */
$Id = 1 ;
/* Création d'un modèle de requête. */
$query = 'UPDATE membres SET passwd = :passwd WHERE id = :id' ;
/* Tableau de valeurs pour PDO. */
$values = [':passwd' => $hash, ' :id' => $Id] ;
/* Exécution de la requête. */
try
{
  $res = $pdo->prepare($query) ;
$res->execute($values) ;
}
catch (PDOException $e)
{
  /* Erreur de requête. */
echo `Query error.' ;
  die() ;
}

Maintenir vos hachages à jour avec la fonction password_needs_rehash()

Pour comprendre cette étape, voyons comment fonctionne password_hash().

password_hash() prend trois arguments :

Le mot de passe que vous devez hacher
L'algorithme de hachage que vous voulez utiliser
Un tableau d'options à passer à l'algorithme de hachage.

PHP supporte différents algorithmes de hachage, mais généralement il est préférable d'utiliser celui par défaut.
Vous pouvez sélectionner l'algorithme par défaut en utilisant la constante PASSWORD_DEFAULT, comme vous l'avez vu dans les exemples précédents.

A partir de juin 2020, l'algorithme par défaut est Bcrypt.

Cependant, PHP peut changer l'algorithme par défaut dans le futur, si un algorithme meilleur et plus sûr est implémenté.

Lorsque cela se produira, la constante PASSWORD_DEFAULT indiquera le nouvel algorithme. Ainsi, tous les nouveaux hachages seront créés à l'aide du nouvel algorithme.

Mais que se passe-t-il si vous voulez prendre tous vos anciens hachages, créés avec l'algorithme précédent, et les recréer automatiquement avec le nouvel algorithme ?

C'est là que la fonction password_needs_rehash() entre en jeu.

La fonction password_needs_rehash() vérifie si un hash a été créé avec un algorithme et des paramètres donnés. Par exemple : vérifier si le password doit être mis à jour

          Copier le code

/* Mot de passe. */
$password = `mon mot de passe secret' ;
/* Fixer le paramètre " cost " à 10. */
$options = ['cost' => 10] ;
/* Créez le hachage. */
$hash = password_hash($password, PASSWORD_DEFAULT, $options) ;
/* Maintenant, changez le cost. */
$options['cost'] = 12 ;
/* Vérifiez si le hash doit être créé à nouveau. */
if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options))
{
  echo 'Vous devez recréer le mot de passe.' ;
}
Si l'algorithme de hachage par défaut actuel est différent de l'algorithme utilisé pour créer le hachage, la fonction password_needs_rehash() renvoie true.

password_needs_rehash() vérifie également si le paramètre options est différent.

Ceci est très pratique si vous voulez mettre à jour vos hachages après avoir modifié un paramètre comme le cost de Bcrypt.

Cet exemple montre comment vous pouvez vérifier automatiquement le hachage d'un mot de passe et le mettre à jour si nécessaire, lorsqu'un utilisateur distant se connecte :

          Copier le code

/* Inclure le script de connexion à la base de données. */
include 'pdo.php' ;
/* Définissez le paramètre "cost" à 12. */
$options = ['cost' => 12] ;
/* Statut de connexion : false = non authentifié, true = authentifié. */
$login = FALSE ;
/* Nom d'utilisateur du formulaire de connexion. */
$username = $_POST['username'] ;
/* Mot de passe du formulaire de connexion. */
$password = $_POST['password'] ;
/* N'oubliez pas de valider $username et $password. */
/* Recherche du nom d'utilisateur dans la base de données. */
$query = 'SELECT * FROM membres WHERE (login = :nom)' ;
/* Tableau de valeurs pour PDO. */
$values = [':nom' => $username] ;
/* Exécution de la requête */
try
{
  $res = $pdo->prepare($query) ;
  $res->execute($values) ;
}
catch (PDOException $e)
{
  /* Erreur de requête. */
echo `Query error.' ;
  die() ;
}
$row = $res->fetch(PDO::FETCH_ASSOC) ;
/* S'il y a un résultat, vérifiez si le mot de passe correspond en utilisant password_verify(). */
if (is_array($row))
{
  if (password_verify($password, $row['passwd']))
  {
    /* Le mot de passe est correct. */
    $login = TRUE ;
	
	/* Vérifiez si le hachage doit être créé à nouveau. */
    if (password_needs_rehash($row['passwd'], PASSWORD_DEFAULT, $options))
    {
      $hash = password_hash($password, PASSWORD_DEFAULT, $options) ;
      
      /* Mise à jour du hachage du mot de passe dans la base de données. */
      $query = 'UPDATE membres SET passwd = :passwd WHERE id = :id' ;
      $values = [':passwd' => $hash, ':id' => $row['id']] ;
      
      try
      {
        $res = $pdo->prepare($query) ;
        $res->execute($values) ;
      }
      catch (PDOException $e)
      {
        /* Erreur de requête. */
		echo `Query error.' ;
        die() ;
      }
    }
  }
}

Par carabde 22 Oct 2021



Voir aussi nos tutoriel :

Objet regexp javascript

Objet regexp javascript : Les expressions régulières sont des modèles utilisés pour vérifier des combinaisons de caractère dans les chaînes de caractères.

Balise header

Définit un en-tête d'un document ou de la section

 Détermine si une variable est définie et est différente de NULL">isset

 Détermine si une variable est définie et est différente de NULL