Contrairement à ce qu’on lit partout, générer une lettre ou une chaîne aléatoire n’est pas une opération triviale dès qu’on sort du contexte « petit script de test ». La différence entre une génération aléatoire suffisante pour un jeu et une génération aléatoire suffisante pour un jeton de sécurité est réelle, et je vois régulièrement les deux confondues en production.
Le cas simple : lettre ou chaîne pour un usage non sensible
Pour un identifiant temporaire, un code de tirage au sort, ou tout usage sans enjeu de sécurité, du PHP basique suffit :
function lettre_aleatoire() {
$alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return $alphabet[random_int(0, 25)];
}
function chaine_aleatoire($longueur = 8) {
$alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$chaine = '';
for ($i = 0; $i < $longueur; $i++) {
$chaine .= $alphabet[random_int(0, strlen($alphabet) - 1)];
}
return $chaine;
}
J'utilise volontairement random_int() plutôt que rand() ou mt_rand(), même pour un usage non sensible. Ça ne coûte rien en performance perceptible sur un usage ponctuel, et ça évite de prendre une mauvaise habitude qui se propage ensuite dans du code plus sensible — j'ai vu ce genre de copier-coller remonter jusqu'à la génération de jetons de réinitialisation de mot de passe chez un client, ce qui n'a rien d'anodin.
Le cas sensible : jetons, mots de passe temporaires, clés
Dès qu'un jeton généré aléatoirement protège quelque chose — accès à un compte, réinitialisation de mot de passe, clé d'API — la fonction doit être cryptographiquement sûre. En PHP, ça veut dire random_bytes() ou random_int(), jamais rand(), mt_rand() ni un générateur basé sur l'horodatage.
function jeton_securise($longueur_octets = 32) {
return bin2hex(random_bytes($longueur_octets));
}
En JavaScript côté navigateur, l'équivalent est crypto.getRandomValues(), et non Math.random(), qui n'offre aucune garantie cryptographique — sa documentation MDN le précise d'ailleurs explicitement depuis des années, mais je continue à voir du code de production qui l'ignore.
function jetonSecurise(longueur = 32) {
const tableau = new Uint8Array(longueur);
crypto.getRandomValues(tableau);
return Array.from(tableau, o => o.toString(16).padStart(2, '0')).join('');
}
Un cas réel de confusion
Un développeur indépendant qui travaillait pour une association bretonne m'a demandé un avis sur un système de récupération de mot de passe qui générait des jetons avec mt_rand() seedé sur l'horodatage. Le jeton de 8 caractères alphanumériques avait un espace de recherche suffisamment restreint, combiné à la prévisibilité de la seed, pour être reconstitué par force brute en quelques minutes sur un poste ordinaire. Le correctif a pris vingt minutes ; le trouver a pris plus longtemps, parce que le code "avait l'air de marcher" depuis des mois sans incident visible.
Le cas des mots de passe temporaires lisibles par un humain
Un cas particulier mérite d'être détaillé : les mots de passe temporaires envoyés par e-mail, qu'un utilisateur doit ensuite recopier. Là, la sécurité cryptographique ne suffit pas seule — un mot de passe généré avec random_bytes() puis encodé en hexadécimal produit une chaîne illisible pour un humain, source d'erreurs de recopie. La bonne pratique consiste à restreindre l'alphabet à des caractères non ambigus (en excluant 0/O, 1/l/I) tout en conservant une source aléatoire cryptographique :
function mot_de_passe_temporaire($longueur = 10) {
$alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
$chaine = '';
for ($i = 0; $i < $longueur; $i++) {
$chaine .= $alphabet[random_int(0, strlen($alphabet) - 1)];
}
return $chaine;
}
Cette fonction reste cryptographiquement sûre (grâce à random_int()) tout en restant utilisable par un humain qui doit la recopier manuellement — un compromis pratique que je recommande systématiquement pour ce cas d'usage précis, distinct du jeton pur destiné à être manipulé uniquement par du code.
Ce que je vérifie systématiquement en audit
Lors d'un audit de code pour une PME ou une association, je recherche systématiquement les occurrences de rand(), mt_rand() et Math.random() dans tout code touchant à l'authentification, aux jetons de session ou aux réinitialisations de mot de passe. C'est une recherche simple (grep -rn "mt_rand|Math.random" .) qui prend quelques minutes et révèle régulièrement des points sensibles hérités d'un développement rapide, jamais repris depuis.
Cas particulier : générer une lettre pour un usage pédagogique
Une association bretonne d'initiation à la programmation, avec laquelle j'échange régulièrement sur des questions techniques, utilise un générateur de lettre aléatoire pour un jeu pédagogique destiné à des collégiens : deviner un mot à partir de lettres révélées une à une. Dans ce contexte précis, la simplicité prime largement sur la robustesse cryptographique — utiliser random_int() reste une bonne pratique par défaut, mais l'enjeu de sécurité y est nul. C'est un bon exemple pour illustrer que la rigueur technique doit rester proportionnée au contexte réel, pas appliquée uniformément par principe : le même niveau d'exigence appliqué à un jeu pédagogique et à un système d'authentification n'a pas de sens économique pour une petite structure aux ressources limitées.
Une note sur les extensions de navigateur qui promettent l'aléatoire "vraiment aléatoire"
Je vois parfois circuler l'idée qu'un générateur aléatoire basé sur du bruit matériel externe (webcam, micro) serait "plus aléatoire" qu'un générateur logiciel standard. Pour un usage web courant, cette distinction n'a pas de sens pratique : les implémentations cryptographiques standards de random_bytes() et crypto.getRandomValues() s'appuient déjà sur des sources d'entropie du système d'exploitation, conçues et auditées spécifiquement pour cet usage. Ajouter une couche supplémentaire de complexité pour un gain théorique non mesurable n'apporte rien en pratique, et introduit surtout une dépendance et une surface d'erreur supplémentaires sans bénéfice réel pour la quasi-totalité des cas d'usage rencontrés en PME.
En résumé
Deux familles de besoins, deux outils : génération simple pour l'aléatoire non sensible, génération cryptographique pour tout ce qui protège un accès. Le coût de passer systématiquement à la version sécurisée est négligeable ; le coût de ne pas le faire ne se voit que le jour où quelqu'un l'exploite.
Pour aller plus loin
Pour une référence externe, consultez les recommandations de l'ANSSI sur la robustesse des mots de passe.