Générer un mot de passe aléatoire sécuritaire

Voici une solution simple et élégante pour générer des mots de passe aléatoires et sécuritaires en PHP. La fonction génère un mot de passe avec les caractéristiques suivantes:

  • Caractères en majuscules et en minuscules
  • Un caractère spécial
  • Supression des caractères semblables I (i majuscule), 1 (Un) et l (L en minuscule) (Suggéré par Antoine !)

/**
 * Generate a random secure password.
 *
 * @param integer $nbChar Password length. Default 8.
 *
 * @access public
 * @author Sylvain Lévesque
 * @return string
 */
function generatePass( integer $nbChar = 8 ) {
    if ( empty($nbChar) ) {
        $nbChar = 8;
    }   
 
    $characters = '023456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ#!$';
    $specials = '#!?$%&*';
 
    $firstPart = substr(str_shuffle($characters), 0, $nbChar - 1);
    $lastPart = substr(str_shuffle($specials), 0, 1);
 
    return str_shuffle($firstPart . $lastPart);
}

Donnez moi votre avis ?

N.B. Vous êtes libre d’utiliser ce bout de code comme bon vous semble.

  • Ça semble bon.

    Simple suggestion: tu pourrais éliminer les caractères qui se ressemblent trop. Exemple: I (i majuscule), 1 (un) et l (L minuscule).

    Si on a à donner le mot de passe par téléphone, une erreur de lecture pourrait causer problème.

  • Merci pour ton commentaire !

    Excellente suggestion que j’ai répliqué dans le bout de code.

    Ciao !

  • Salut Sylvain,

    Ton algo se base un générateur pseudo-aléatoire… str_shuffle doit utiliser quelque chose comme rnd() en c++. Il est donc possible que le même mot de passe soit générer 2 fois ou même plus. Une faiblesse dans l’algo pseudo-aléatoire pourrait mettre en évidence que certain mot de passe sont générés plus souvent que d’autres. Le hacker pourrait essayer ces mots de passe en premier puisqu’ils ont plus de chance d’être générés…

    Idéalement il faut que tu essaies de créer un algo qui ne génère jamais le même mot de passe 2 fois (ou sinon très rarement). Les fonctions de hachage peuvent aider à atteindre ton but mais il existe des méthodes plus sophistiquées:

    https://www.grc.com/passwords.htm

    🙂

  • Merci Pierre-Yves pour ton commentaire. J’aime bien ce genre de commentaire geek ! 🙂

    Selon la documentation officiel de PHP que l’on peut retrouver ici dans la fonction shuffle:

    http://ca3.php.net/manual/fr/function.shuffle.php

    Le générateur de nombres aléatoires est initialisé automatiquement depuis la version PHP 4.2.0. Personnellement je suis sur PHP 5.2.6 et j’espère que tout le monde est rendu à au moins PHP 5. À moins que je me trompe, et pour ça il faudrait vérifier la source en C (C++) de PHP, ceci n’est plus un algo pseudo-aléatoire.

    Une avenue possible pour éviter ce problème pourrait être de tenir un cache des mots de passe générés et de vérifier contre cette liste pour l’unicité du mot de passe généré. Ça enlève la simplicité et l’élégance de la solution par contre.

    Ça dépend de l’utilisation que nous voulons faire des mots de passe générés. Dans mon cas, c’est des mots de passe temporaires destinés à être modifiés par l’utilisateur ensuite. Par exemple pour un formulaire d’inscription.

    Merci pour le lien je vais le lire avec attention !

  • Effectivement! Une solution avec une fonction de hachage rend le code beaucoup plus difficile à lire 🙁

    Est-ce que tes utilisateurs peuvent demander un « reset » de leur mot de passe pour les cas où il l’aurait oublié?

    Mais effectivement, tout dépend de l’utilisation que tu en fais…

  • À vrai dire rajouter une fonction de hachage est ultra simple en PHP.

    $password = md5( 'MonSuperMotDePasseAHacher' );

    ou encore mieux:

    $password = sha1( 'MonSuperMotDePasseAHacher' );

    Mais veux-tu bien me dire qui pourrait retenir un mot de passe de la longeur d’un hash ? Pas très pratique. Mais je suis d’accord avec le fait qu’il serait plus sécuritaire.

    Pour le reset de mot de passe, c’est exactement le genre d’application de cette fonction.

    Pour ajouter un plus encore on pourrait se constituer un dictionnaire de mots et vérifier contre cette liste le mot de passe généré. En plus du cache de mots de passe ça serait pas mal du tout !

  • Tu es tombé sur l’un de mes dadas, la sécurité informatique! 😀

    J’ai trouvé ici une super fonction en PHP:
    http://ca3.php.net/manual/fr/function.uniqid.php

    $better_token = md5(uniqid(mt_rand(), true));

    Si tu génères 5 fois (ou plus au besoin) ce token, tu obtiens une longue string du genre:

    4DF2AB05DFFC3E349295….B6097E8A3CAD492B

    Si tu fais des paires avec chaque caractères, tu obtiens un chiffre en hexa entre 0 et 255:

    4D = 77
    DF = 223
    F2 = 242
    etc..

    Ces valeurs pourraient devenir des indexes qui serviraient à choisir un lettre dans un tableau de caractères:

    Pour le tableau:

    023456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ#!$

    77 = f (J’ai fait un tour complet du tableau + 14)
    223 = C
    242 = W

    … tu obtiens ton mot de passe unique « fCW…. »

    Note:
    Idéalement tu devrais ajoutez un « sel » sur ton token afin d’éviter les problèmes au caus où ton token est généré trop rapidement:

    $better_token = md5(uniqid(mt_rand(), true) ^ str_shuffle($uneChainedeCaractereTresLongue));

    J’aurais aimé fournir le code en PHP mais mon php ne s’est pas levé ce matin 😉

    Qu’est-ce que tu en penses?

  • Oups… le XOR c’est pas ^ mais bien XOR il semblerait… du genre V1 XOR V2…

    Pis au lieu de prendre seulement 2 caractères pour générer les index, tu es mieux avec 4, comme ça tu auras plus « d’unicité »….

    dsl pour mon petit côté obsessif! 😀

  • À propos du reset de mot de passe… Si un pattern était découvert dans l’algo pseudo-aléatoire, le hacker n’a qu’à demander à répétition un reset du mot de passe et tester les mots de passe les plus probablement entre chaque reset…

  • Bon.. finalement c’est Dave qui avait la solution!

    http://ca.php.net/manual/fr/function.crypt.php

    Keep it simple!

  • Donc ça donnerait !

    /**
     * Generate a random secure password.
     *
     * @param integer $nbChar Password length. Default 8.
     *
     * @access public
     * @author Sylvain Lévesque
     * @return string
     */
    function generatePass( integer $nbChar = 8 ) {
        if ( empty($nbChar) ) {
            $nbChar = 8;
        }   
     
        return substr(crypt(uniqid(mt_rand(), true), 0, $nbChar);
    }
  • Hervé

    Avec cette version, l’utilisation d’au moins un caractère de chaque catégorie (chiffres, minuscules, majuscules, spéciaux) est utilisé. L’utilisation de caractères spéciaux devient optionnel également, mais reste par défaut.


    function generatePass($nbChar = 8, $useSpecials = true) {
    $char_num = '023456789';
    $char_min = 'abcdefghijkmnopqrstuvwxyz';
    $char_maj = 'ABCDEFGHJKLMNOPQRSTUVWXYZ';
    $specials = '#!?$%&*';
    $char_complete = $char_num.$char_min.$char_maj;

    $numPart = substr(str_shuffle($char_num), 0, 1);
    $minPart = substr(str_shuffle($char_min), 0, 1);
    $majPart = substr(str_shuffle($char_maj), 0, 1);

    if($useSpecials){
    if($nbChar>3){
    $specialsPart = substr(str_shuffle($specials), 0, 1);

    $complete = substr(str_shuffle($char_complete.$specials), 0, $nbChar - 4);

    $pass = str_shuffle($numPart.$minPart.$majPart.$specialsPart.$complete);
    }
    else
    $pass = substr(str_shuffle($char_complete.$specials),0,$nbChar);
    }
    else{
    if($nbChar>2){
    $complete = substr(str_shuffle($char_complete), 0, $nbChar - 3);

    $pass = str_shuffle($numPart.$minPart.$majPart.$complete);
    }
    else
    $pass = substr(str_shuffle($char_complete),0,$nbChar);
    }

    return $pass;
    }