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 --buildVous 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 :
- Allez dans le dossier du projet
- Ouvrez le fichier
docker-compose.yml - Dans la section port, vous devriez avoir des notations comme suit :
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3- 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/3Lorsque 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 :
- Allez dans
File>Settings>Languages & Frameworks>PHP - Cliquez sur les
...à côté du champCLI Interpreter - Cliquez sur le
+en haut à gauche pour ajouter un nouvel interpréteur - Sélectionnez
From Docker, Vagrant, WSL, Remote... - Sélectionnez
Docker Compose - Si le champ
Serverest vide, cliquez sur les...à côté et laissez les options par défaut, puis cliquez surOK - Dans le champ
Configuration file, cliquez sur les..., retirez tout le contenu et sélectionnez le fichierdocker-compose.ymlà la racine du dossier actuel :003-php-passwords/docker-compose.yml - Dans le champ
Service, sélectionnezphp - Cliquez sur
OKpour valider l'ajout de l'interpréteur - Cliquez sur
ApplypuisOKpour 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
- 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. - Dans cette fonction, utilisez une boucle pour générer les options allant de 8 à 42.
- Pour chaque option, si la valeur correspond à la valeur sélectionnée, ajoutez l'attribut
selected. - 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
- 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) - Utilisez le Null Coalescing Operator pour définir des valeurs par défaut si certaines données ne sont pas présentes.
- Affichez les valeurs récupérées pour vérifier que tout fonctionne correctement. (Vous pourrez supprimer cette étape plus tard.)
- Si la methode HTTP n'est pas
POST, définissez des valeurs par défaut pour chaque option. Vous pouvez utiliser la variable superglobale$_SERVERpour récupérer la méthode HTTP. (documentation: https://www.php.net/manual/en/reserved.variables.server.php) - 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 FONCTIONAstuce
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
- Implémentez la fonction
generateRandomCharacterqui prend une chaîne de caractères en paramètre et retourne un caractère aléatoire de cette chaîne. - Implémentez la fonction
generatePasswordqui suit l'algorithme décrit ci-dessus. - Appelez la fonction
generatePasswordavec les paramètres récupérés du formulaire pour générer le mot de passe. - 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 :
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
- Récupérez le mot de passe saisi depuis la variable superglobale
$_POST. - Affichez la valeur récupérée pour vérifier que tout fonctionne correctement. (Vous pourrez supprimer cette étape plus tard.)
- Dans le HTML, faites en sortes que la zone de texte conserve sa valeur après la soumission du formulaire.
- Si la methode HTTP n'est pas
POST, définissez une valeur par défaut vide pour le mot de passe. - Créez deux variables pour contenir le score ("-" par défaut) et la liste des messages de validation ("-" par défaut).
- 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
- Créez une fonction
validatePasswordqui prend en paramètre le mot de passe à valider, et par référence les variables score et messages, et qui retourne rien. - Dans cette fonction, utilisez des expressions régulières avec
preg_matchpour vérifier la présence de chaque type de caractère (majuscules, minuscules, chiffres, symboles). - Pour chaque type de caractère présent, augmentez le score de 1, sinon ajoutez un message d'erreur à la liste des messages.
- On augmentera aussi le score si le mot de passe fait au moins 8 caractères.
- Appelez la fonction
validatePasswordavec le mot de passe récupéré du formulaire, et les variables score et messages. - Affichez le score et les messages dans la zone prévue à cet effet dans la page HTML.
- 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,\dest équivalent à[0-9]) - Pour les symboles :
/[\W]/(Contient au moins un symbole,\West é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
- Relire la documentation de
preg_matchet particulièrement son troisième paramètre$matches: https://www.php.net/manual/en/function.preg-match.php - Modifiez la fonction
validatePasswordpour utiliser une seule expression régulière avec des lookaheads pour vérifier la présence de chaque type de caractère. - Pour chaque type de caractère présent, augmentez le score de 1, sinon ajoutez un message d'erreur à la liste des messages.
- On augmentera aussi le score si le mot de passe fait au moins 8 caractères.
- 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.