Adrien Gras
Ateliers

Générateur de mots de passe

Un générateur de mots de passe pour pratiquer boucles, fonctions, chaînes et regex.

Le but de cet atelier est de vous familiariser avec les bases de PHP en créant un générateur de mots de passe. Vous allez apprendre à manipuler des variables, à utiliser des boucles, des fonctions et à manipuler des chaînes de caractères.

Préparation

Attention

Si vous aviez déjà un projet en cours, assurez-vous de bien stopper les conteneurs Docker des projets précédents pour éviter les conflits de ports.

Vous trouverez la procédure ici : Changer de projet

Lancer la stack Docker

Rendez-vous dans le dossier racine du dépôt git puis dans 003-php-passwords. En suite, lancez la stack docker avec la commande suivante :

docker compose up -d --build

Vous pouvez en suite vérifier que tout fonctionne en allant à l'adresse http://localhost:80.

Attention

Si vous avez une erreur HTTPS, acceptez simplement le certificat auto-généré.

Attention

Pour ceux qui ont le problème de page qui ne se raffraichissent pas sur Windows

Vous allez devoir changer votre configuration docker pour ce projet. Pour se faire :

  1. Allez dans le dossier du projet
  2. Ouvrez le fichier docker-compose.yml
  3. Dans la section port, vous devriez avoir des notations comme suit :
    ports:
      - "80:80" # HTTP
      - "443:443" # HTTPS
      - "443:443/udp" # HTTP/3

  1. Changez les ports de gauche (ceux avant les :) pour des ports libres sur votre machine, par exemple :
    ports:
      - "8080:80" # HTTP
      - "444:443" # HTTPS
      - "444:443/udp" # HTTP/3

Lorsque dans la documentation il est fait référence à http://localhost ou https://localhost, vous devrez utiliser les ports que vous avez configurés : https://localhost:444 pour accéder à votre projet.

Ouvrir le projet dans PHPStorm

Ouvrez PHPStorm, puis :

  • Si vous êtes déjà dans un projet :
    • File > Open
    • Sélectionnez le dossier racine du dépôt git > 003-php-passwords/app
  • Si vous n'êtes pas dans un projet :
    • Open
    • Sélectionnez le dossier racine du dépôt git > 003-php-passwords/app

Configurer l'interpréteur PHP

Pour éviter que vous n'ayez à installer PHP sur votre machine, nous allons utiliser l'interpréteur PHP fourni par Docker.

Pour cela :

  1. Allez dans File > Settings > Languages & Frameworks > PHP
  2. Cliquez sur les ... à côté du champ CLI Interpreter
  3. Cliquez sur le + en haut à gauche pour ajouter un nouvel interpréteur
  4. Sélectionnez From Docker, Vagrant, WSL, Remote...
  5. Sélectionnez Docker Compose
  6. Si le champ Server est vide, cliquez sur les ... à côté et laissez les options par défaut, puis cliquez sur OK
  7. Dans le champ Configuration file, cliquez sur les ..., retirez tout le contenu et sélectionnez le fichier docker-compose.yml à la racine du dossier actuel : 003-php-passwords/docker-compose.yml
  8. Dans le champ Service, sélectionnez php
  9. Cliquez sur OK pour valider l'ajout de l'interpréteur
  10. Cliquez sur Apply puis OK pour fermer la fenêtre des paramètres

Les bases

Consigne

Comme dans l'atelier précédent, commencez par afficher une page HTML simple avec un titre "Générateur de mots de passe" en utilisant la syntaxe heredoc en PHP.

Ajouter les éléments de base

Maintenant que nous avons une page HTML qui s'affiche, nous allons ajouter les éléments graphiques nécessaires.

Consigne

Modifiez votre code pour ajouter les éléments suivants à votre page HTML :

  • Une zone pour afficher le mot de passe généré.
  • Des cases à cocher pour inclure ou exclure les types de caractères suivants : majuscules, minuscules, chiffres, symboles.
  • Un bouton pour générer le mot de passe.

Ajuster la taille du mot de passe

Il faudrait, en plus des caractères à utiliser, pouvoir choisir la taille de notre mot de passe.

La meilleure option serait un select simple avec des options allant de 8 à 42.

...Mais écrire les options à la main serait fastidieux. Nous allons donc générer les options en PHP avant d'afficher la page HTML.

Consigne

  1. Créez une fonction generateSelectOptions ; elle prendra en paramètre un entier qui sera la valeur actuellement sélectionnée, ou 10 par défaut.
  2. Dans cette fonction, utilisez une boucle pour générer les options allant de 8 à 42.
  3. Pour chaque option, si la valeur correspond à la valeur sélectionnée, ajoutez l'attribut selected.
  4. Appelez cette fonction dans votre code avant d'afficher la page HTML, et insérez le résultat dans le <select> de votre page.

Mettre en place le formulaire

Maintenant que toutes nos options sont prêtes, il est temps de mettre en place le formulaire pour envoyer les données au serveur.

Consigne

Entourez les éléments de votre page HTML avec une balise <form> et ajoutez un bouton de soumission pour générer le mot de passe.

Le formulaire doit utiliser la méthode POST et envoyer les données à la même page (/).

Du côté script PHP, vous devrez récupérer les données envoyées par le formulaire.

Comme les checkbox peuvent ne pas être cochées, utilisez la notation Null Coalescing Operator pour définir des valeurs par défaut.

Consigne

  1. Récupérez les données envoyés par le formulaire depuis la variable superglobale $_POST. (documentation: https://www.php.net/manual/en/reserved.variables.php)
  2. Utilisez le Null Coalescing Operator pour définir des valeurs par défaut si certaines données ne sont pas présentes.
  3. Affichez les valeurs récupérées pour vérifier que tout fonctionne correctement. (Vous pourrez supprimer cette étape plus tard.)
  4. Si la methode HTTP n'est pas POST, définissez des valeurs par défaut pour chaque option. Vous pouvez utiliser la variable superglobale $_SERVER pour récupérer la méthode HTTP. (documentation: https://www.php.net/manual/en/reserved.variables.server.php)
  5. Dans le HTML, faites en sortes que les cases à cocher et le select conservent leurs valeurs après la soumission du formulaire.

Astuce

Pour éviter de faire de l'interpolation dans des chaînes heredoc, vous pouvez préparer des variables avant d'afficher la page HTML.

Par exemple, pour une case à cocher :

$isUpperCaseChecked = $formUpperCase == 1 ? 'checked' : '';

Puis dans le HTML :

<input type="checkbox" name="uppercase" value="1" {$isUpperCaseChecked}>

Si votre case est cochée lors de la soumission, l'attribut checked sera ajouté à la page HTML. Vous garderez donc la valeur cochée.

Générer le mot de passe

Maintenant que nous avons toutes les options nécessaires, il est temps de générer le mot de passe.

Une manière simple serait de ranger tout les caractères possibles dans une chaîne, puis de tirer au hasard des caractères dans cette chaîne pour construire le mot de passe. Mais cette solution a un problème : elle ne garantit pas que chaque type de caractère sélectionné soit présent dans le mot de passe final, et statistiquement, les chiffres et symboles risquent de ne pas apparaître souvent.

Une méthode plus robuste serait de générer le mot de passe en plusieurs étapes.

Voici l'algorithme que nous allons suivre :

FONCTION prendre_caractère_au_hasard(CHAINE) RETOURNE CHAINE
DEBUT
    INDEX_ALEATOIRE = générer_un_nombre_au_hasard(0, TAILLE_DE_LA_CHAINE - 1)
    RETOURNER CHAINE[INDEX_ALEATOIRE]
FIN FONCTION

FONCTION générer_mot_de_passe(TAILLE, MAJUSCULE, MINUSCULE, CHIFFRES, SYMBOLES) RETOURNE CHAINE
DEBUT
    PASSWORD = ""

    // on crée un tableau de séquence vide
    SEQUENCES = []

    // Pour chaque type de caractère sélectionné, on ajoute la séquence correspondante au tableau
    SI MAJUSCULE = 1
    ALORS
        SEQUENCES[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    FIN SI
    // Même chose pour minuscules, chiffres, symboles

    // on va s'assurer qu'au moins un caractère de chaque type sélectionné soit présent
    // puis on va faire une boucle pour remplir le reste du mot de passe

    // en partant du principe que les checkbox renvoient 1 si cochées, 0 sinon
    LIMITE_BOUCLE = TAILLE_MOT_DE_PASSE - (MAJUSCULE + MINUSCULE + CHIFFRES + SYMBOLES)

    // puis, on va prendre un caractère au hasard pour chaque type sélectionné
    SI MAJUSCULE = 1
    ALORS
        PASSWORD += prendre_caractère_au_hasard(SEQUENCES[0])
    FIN SI
    // Même chose pour minuscules, chiffres, symboles

    // on remplit le reste du mot de passe
    POUR I DE 1 À LIMITE_BOUCLE
        // on choisit une séquence au hasard dans le tableau
        SEQUENCE_ALEATOIRE = SEQUENCES[ générer_un_nombre_au_hasard(0, TAILLE_DE_SEQUENCES - 1) ]
        PASSWORD += prendre_caractère_au_hasard(SEQUENCE_ALEATOIRE)
    FIN POUR

    // et on mélange le mot de passe pour éviter que les caractères obligatoires soient toujours au début
    PASSWORD = mélanger_chaîne(PASSWORD)

    RETOURNER PASSWORD
FIN FONCTION

Astuce

Vous remarquerez que certaines fonctions comme générer_un_nombre_au_hasard ou mélanger_chaîne ne sont pas définies. Vous devrez utiliser les fonctions PHP appropriées pour réaliser ces actions.

Consigne

  1. Implémentez la fonction generateRandomCharacter qui prend une chaîne de caractères en paramètre et retourne un caractère aléatoire de cette chaîne.
  2. Implémentez la fonction generatePassword qui suit l'algorithme décrit ci-dessus.
  3. Appelez la fonction generatePassword avec les paramètres récupérés du formulaire pour générer le mot de passe.
  4. Affichez le mot de passe généré dans la zone prévue à cet effet dans la page HTML.

Bonus : Validateur de sécurité de mot de passe

Pour cette seconde partie, nous allons retourner le sujet et tenter de valider la sécurité du mot de passe généré.

Attention

Cette partie va aborder le sujet des Expressions Régulières (RegEx).

C'est un concept de base de l'informatique qui peut paraitre complexe au début, mais qui est très puissant une fois maîtrisé.

Il ne faudra surtout pas hésiter à consulter les documentations liées, faire des recherches sur internet, et poser des questions si vous êtes bloqués.

Voici quelques outils en ligne pour vous aider :

  • Regex101 : Un outil en ligne pour tester et déboguer des expressions régulières.
  • RegExr : Un autre outil en ligne pour apprendre, construire et tester des expressions régulières.
  • Regexper : Un visualiseur d'expressions régulières qui transforme les regex en diagrammes.

Préparation

Copiez le fichier app/index.php dans un nouveau fichier app/validator.php puis éliminez tout le code lié au formulaire et à la génération de mot de passe.

Mise en place

Consigne

Modifiez votre code pour ajouter les éléments suivants à votre page HTML :

  • Une zone de texte pour saisir le mot de passe à valider.
  • Un bouton pour valider le mot de passe.
  • Une zone pour afficher le résultat de la validation (un score et une liste de messages).

Maintenant que côté HTML est prêt, il est temps de récupérer le mot de passe saisi et de le valider.

Consigne

  1. Récupérez le mot de passe saisi depuis la variable superglobale $_POST.
  2. Affichez la valeur récupérée pour vérifier que tout fonctionne correctement. (Vous pourrez supprimer cette étape plus tard.)
  3. Dans le HTML, faites en sortes que la zone de texte conserve sa valeur après la soumission du formulaire.
  4. Si la methode HTTP n'est pas POST, définissez une valeur par défaut vide pour le mot de passe.
  5. Créez deux variables pour contenir le score ("-" par défaut) et la liste des messages de validation ("-" par défaut).
  6. Affichez le score et les messages dans la zone prévue à cet effet dans la page HTML.

Validation du mot de passe - méthode "simple"

Une méthode "naïve" pour valider un mot de passe serait de vérifier la présence de chaque type de caractère avec des boucles et des conditions. Mais ça serait hautement inefficace et fastidieux.

A la place, nous allons utiliser des expressions régulières (RegEx) pour vérifier la présence de chaque type de caractère.

Astuce

Les expressions régulières (ou regex) sont des chaînes de caractères qui définissent un motif de recherche. Elles permettent de rechercher, valider ou manipuler du texte selon des règles précises, comme vérifier qu’un mot de passe contient des majuscules, des chiffres ou des symboles.

En PHP, elles sont utilisées avec des fonctions comme preg_match(), preg_replace() ou preg_split().

Pourquoi les utiliser ?

  • Validation : Vérifier qu’une chaîne respecte un format (email, mot de passe, numéro de téléphone).
  • Extraction : Récupérer des sous-parties d’un texte (ex : extraire un code postal dans une adresse).
  • Remplacement : Modifier dynamiquement du contenu (ex : masquer des données sensibles).
  • Flexibilité : Une seule regex peut remplacer des dizaines de lignes de code conditionnel.

En plus d’être universelles — utilisées aussi bien en PHP, JavaScript, Python qu’en commandes Bash comme grep ou sed —, les expressions régulières sont souvent optimisées pour la performance, ce qui en fait un choix privilégié pour traiter rapidement de gros volumes de texte, à condition de les écrire avec soin pour éviter les motifs gourmands.

Bien que souvent redoutées par les développeurs juniors en raison de leur syntaxe cryptique, les expressions régulières deviennent, une fois maîtrisées, un outil hyper puissant pour manipuler et valider du texte avec une précision et une efficacité inégalées.

Si vous voulez une introduction rapide aux regex, vous pouvez regarder ce short : REGEX 😍 - Youtube - V2F.

Pour aller plus loin

Si vous voulez voir comment CloudFlare, pourtant l'une des entreprises les plus importante de la tech, plante sa prod avec des regex mal optimisées, c'est par ici :

Consigne

  1. Créez une fonction validatePassword qui prend en paramètre le mot de passe à valider, et par référence les variables score et messages, et qui retourne rien.
  2. Dans cette fonction, utilisez des expressions régulières avec preg_match pour vérifier la présence de chaque type de caractère (majuscules, minuscules, chiffres, symboles).
  3. Pour chaque type de caractère présent, augmentez le score de 1, sinon ajoutez un message d'erreur à la liste des messages.
  4. On augmentera aussi le score si le mot de passe fait au moins 8 caractères.
  5. Appelez la fonction validatePassword avec le mot de passe récupéré du formulaire, et les variables score et messages.
  6. Affichez le score et les messages dans la zone prévue à cet effet dans la page HTML.
  7. Si le script n'est pas appellé en POST, le score doit être "-" et les messages doivent être "-".

Astuce

Vous pouvez utiliser les regex suivantes :

  • Pour les majuscules : /[A-Z]/ (Contient au moins une lettre majuscule, de A à Z)
  • Pour les minuscules : /[a-z]/ (Contient au moins une lettre minuscule, de a à z)
  • Pour les chiffres : /[\d]/ (Contient au moins un chiffre, \d est équivalent à [0-9])
  • Pour les symboles : /[\W]/ (Contient au moins un symbole, \W est équivalent à [... tout ce qui n'est pas une lettre ou un chiffre ...])

Vous pouvez consulter la documentation de preg_match ici : https://www.php.net/manual/en/function.preg-match.php

Validation du mot de passe - méthode "Crackozaurus Rex 3000"

Une particularité des RegEx est qu'elles peuvent être combinées pour créer des motifs plus complexes. On peut notamment utiliser motifs particulier pour rechercher l'existence de plusieurs critères en une seule passe. On appelle ça des "lookaheads".

Astuce

Les lookaheads sont des assertions qui vérifient si un certain motif est présent dans la chaîne, sans consommer de caractères. C'est-à-dire qu'ils "regardent en avant" dans la chaîne pour voir si le motif existe, sans avancer le pointeur de position.

Par exemple, l'expression /(?=.*[A-Z])(?=.*[a-z])(?=.*[\d])(?=.*[\W])/ vérifie si la chaîne contient au moins une majuscule, une minuscule, un chiffre et un symbole.

Pour chaque motif ici :

  • ?= est le début d'un lookahead positif. (on regarde dans toute la chaine)
  • .* signifie "n'importe quel caractère, n'importe quel nombre de fois" (on peut avoir des caractères avant le motif)
  • [A-Z], [a-z], [\d], [\W] sont les motifs que l'on cherche (majuscules, minuscules, chiffres, symboles)

Les lookaheads sont particulièrement utiles pour valider des chaînes qui doivent respecter plusieurs critères simultanément, comme les mots de passe complexes, et permettent de le faire de manière concise et efficace.

Consigne

  1. Relire la documentation de preg_match et particulièrement son troisième paramètre $matches : https://www.php.net/manual/en/function.preg-match.php
  2. Modifiez la fonction validatePassword pour utiliser une seule expression régulière avec des lookaheads pour vérifier la présence de chaque type de caractère.
  3. Pour chaque type de caractère présent, augmentez le score de 1, sinon ajoutez un message d'erreur à la liste des messages.
  4. On augmentera aussi le score si le mot de passe fait au moins 8 caractères.
  5. Testez votre fonction pour vérifier qu'elle fonctionne correctement.

Astuce

Le tableau $matches contiendra les résultats des lookaheads. Vous pouvez l'utiliser pour déterminer quels types de caractères sont présents dans le mot de passe.

Pour la RegEx ci-dessus, $matches[1] contiendra quelque chose si la majuscule est présente, sinon sera vide. Même chose pour $matches[2] (minuscules), $matches[3] (chiffres) et $matches[4] (symboles).

Vous remarquerez que l'index 0 contient toujours la chaîne complète.

Sur cette page