D’abord la conclusion : Math.random() est un générateur de nombres pseudo-aléatoires ordinaire. MDN écrit qu’il ne fournit pas de nombres aléatoires cryptographiquement sûrs et qu’il ne faut pas l’utiliser pour quoi que ce soit lié à la sécurité. Les mots de passe de connexion, les jetons à usage unique et les clés ont besoin de crypto.getRandomValues(). Une chaîne qui semble mélangée, un bouton Générer qui marche hors ligne, et un cadenas dans la barre d’adresse prouvent chacun une autre frontière. Aucun ne répond à « quelle API Générer a-t-il appelée ».
Répondre à l’intention de recherche en une phrase
Math.random() renvoie un flottant dans [0, 1) selon une distribution approximativement uniforme. MDN est net : cette valeur n’est pas cryptographiquement sûre ; utilisez plutôt Crypto.getRandomValues(). « Assez mélangé à l’œil » décrit le mélange de caractères en sortie. Cela ne dit rien sur la possibilité, pour quelqu’un qui a vu quelques sorties — ou le code source — de reconstituer l’état interne du générateur et d’imprimer les candidats suivants.
Cette note s’adresse aux développeurs et à l’exploitation qui relisent un « générateur de mot de passe en ligne », et à quiconque a reçu d’un collègue un onglet Générer à inspecter avant de s’en servir. Elle ne remplace pas le mode d’emploi du générateur de mot de passe, et ne répète pas le parcours Network de « le clair a-t-il quitté cet onglet ». Ce qu’il faut emporter : la couche qui sépare un PRNG ordinaire d’un CSPRNG, pourquoi un modulo naïf peut encore biaiser une bonne API, et le chemin exact depuis le clic sur Générer jusqu’à la pile d’appels.
Sembler mélangé dit seulement que les caractères peuvent être hétérogènes
Juger « aléatoire » à l’œil est bon marché, et le plus souvent faux. aB3$kP9x et Tr0ub4dor&3 ressemblent tous deux à des mots de passe. Le premier peut venir d’un jeu de caractères large. Le second peut n’être qu’une phrase mémorable avec des substitutions courantes. L’apparence répond à « cela ressemble-t-il à un mot de passe », pas à « le bit suivant peut-il être calculé ».
Même lorsque le jeu contient vraiment majuscules, minuscules, chiffres et symboles, une suite d’indices prévisible réduit encore le travail. Un attaquant capable de relancer le même générateur — ou de retrouver son état après quelques sorties observées — énumère les candidats restants au lieu de tout l’espace. Pour un mot de passe de connexion, l’imprévisibilité vient avant « il y a un caractère spécial ».
La longueur reste une autre question. Le même CSPRNG à 6 caractères et à 16 caractères change le coût d’une force brute de plusieurs ordres de grandeur. En dessous de 8, l’espace est déjà petit même si l’API est la bonne. Le mode aléatoire de FastPwd autorise 6–128 caractères, 16 par défaut, et signale en dessous de 8 une sécurité plus faible — c’est un avertissement de longueur, pas un algorithme plus faible. Gardez les deux affirmations séparées : l’API répond à l’imprévisibilité ; la longueur répond à l’espace de recherche.
Quel type d’aléa Math.random est réellement
ECMAScript spécifie Math.random() comme un Number supérieur ou égal à 0 et strictement inférieur à 1, tiré avec un algorithme ou une stratégie défini par l’implémentation et approximativement uniforme. La spécification du langage n’exige ni une résistance à la prédiction, ni que la graine vienne du bassin d’entropie du système d’exploitation. Un moteur peut choisir un PRNG aux excellentes statistiques et à l’état réversible.
Le moteur V8 de Chrome a utilisé MWC1616 jusqu’à fin 2015. L’article de V8 consigne les limites : 64 bits d’état interne, une qualité biaisée, et des échecs dans la suite TestU01. À partir de V8 4.9.41 (livré avec Chrome 49), il est passé à xorshift128+ : 128 bits d’état, période 2^128 − 1, et un succès sur TestU01. Firefox et Safari ont ensuite rejoint la même famille. Ce sont des progrès statistiques, pas une mise à niveau de sécurité.
Le même article V8 trace la limite en une phrase : xorshift128+ est bien meilleur que l’ancienne implémentation et n’est toujours pas cryptographiquement sûr. Hachage, signatures et chiffrement/déchiffrement ne sont pas le métier d’un PRNG ordinaire ; ils appartiennent à window.crypto.getRandomValues de l’API Web Cryptography. « Sembler statistiquement mélangé » et « un observateur ne peut pas reconstituer à bas coût les sorties suivantes » sont deux barres différentes. Un mot de passe a besoin de la seconde.
La requête CodeQL de GitHub Insecure randomness signale Math.random utilisé pour fabriquer un mot de passe comme une alerte, pour la même raison : si un attaquant peut prédire la valeur, il peut se connecter à la place du nouvel utilisateur. Un générateur qui n’appelle que Math.random() puis projette sur un jeu de caractères peut passer un coup d’œil et un contrôle de fréquences grossier, puis échouer à la relecture qui suppose que quelqu’un a lu le source ou intercepté quelques sorties. N’écrivez pas « il a utilisé Math.random, mais le jeu est large » comme une validation.
Ce que getRandomValues change réellement
crypto.getRandomValues(typedArray) remplit un tableau typé d’entiers — Uint8Array, Uint32Array et les autres vues entières — de valeurs aléatoires au sens cryptographique. MDN est explicite sur le compromis d’implémentation : pour la performance, les navigateurs ne lisent en général pas un générateur matériel à chaque bit. Ils amorcent un PRNG adapté à un usage cryptographique à partir d’une source d’entropie système, comme /dev/urandom sous Unix ou l’interface aléatoire de Windows. L’algorithme peut changer d’un navigateur à l’autre. L’objectif de conception, lui, ne change pas : une sortie déjà publiée ne doit pas permettre de reconstituer à bas coût l’état interne.
Les deux API ne relèvent pas de la même spécification. Math.random relève d’ECMA-262. getRandomValues relève de Web Cryptography. Un seul appel de plus de 65 536 octets lève QuotaExceededError ; un mot de passe de 16 caractères est loin de ce plafond. getRandomValues est aussi la seule méthode de Crypto qui peut s’exécuter hors contexte sécurisé ; la dérivation de clé et subtle.encrypt exigent toujours un contexte sécurisé (HTTPS sur une page de production). « Cela marche encore en HTTP » n’est pas une raison d’abandonner TLS.
Séparez « cet onglet a tiré les octets aléatoires » de « le serveur les a tirés et renvoyés ». Même si le serveur a utilisé un CSPRNG de haute qualité, le mot de passe a déjà séjourné dans la mémoire de l’autre côté, les journaux et cet échange HTTP — en clair ou sous une forme réversible. Le cadenas de la barre d’adresse ne chiffre que le trajet de ce navigateur jusqu’à l’extrémité TLS courante. Il ne dit pas que cette extrémité ne peut pas lire le corps. Si la page affirme « généré en local », Générer ne doit pas envoyer le mot de passe en POST puis le réafficher.
| Ce que vous avez vu | Ce que cela peut soutenir | Ce que cela ne peut pas soutenir |
|---|---|---|
| La sortie semble mélangée | Le jeu de caractères peut être large | Le processus est imprévisible |
Le source contient Math.random |
La page utilise un PRNG ordinaire quelque part | Générer s’en sert pour le mot de passe |
Le source contient getRandomValues |
La page a importé une API CSPRNG | Ce clic a utilisé cette API |
| Générer marche encore hors ligne | Ce clic n’a pas attendu une API de fabrication en direct | La source n’est pas Math.random |
| La recherche Network rate le mot de passe | Cette observation ne l’a pas envoyé comme donnée métier | L’API d’aléa est un CSPRNG |
Biais de modulo : un CSPRNG peut encore être faussé
La deuxième erreur courante arrive après que le script a déjà appelé getRandomValues. Il prend un octet dans 0–255 et écrit charset[byte % charset.length]. Si la longueur du jeu ne divise pas 256, les premiers caractères reçoivent une tranche supplémentaire du reste et apparaissent un peu plus souvent. Sur un mot de passe de 16 caractères, le biais est en général petit, mais il montre que l’implémentation n’a pas fini « uniforme ». Un jeu plus étrange, ou la projection de 8 bits sur une grande liste de mots, rend le biais plus visible.
L’échantillonnage par rejet le corrige : calculez le plus grand multiple de la taille du jeu qui tient encore dans 256 (ou 2^32), écartez tout tirage supérieur ou égal à cette limite, puis prenez le modulo. Les caractères aléatoires et les phrases lisibles de FastPwd appellent tous deux crypto.getRandomValues, tirent un entier 32 bits avec Uint32Array(1), fixent la limite à Math.floor(0x100000000 / max) * max, et bouclent lorsque le tirage tombe dans la queue. Si getRandomValues manque, la génération est refusée — il n’y a pas de repli vers Math.random. Quand vous relisez un autre site, continuez à lire « tirer un entier → écarter la queue → projeter un caractère ». Trouver le nom de la fonction n’est pas la fin de la relecture.
N’expérimentez pas avec un vrai mot de passe de connexion, une clé API de production ou un ticket non masqué. Générez une chaîne jetable et jetez-la. Vous contrôlez le chemin de Générer et le trafic, pas un nouveau secret exposé une seconde fois.
Vérifier maintenant : source, console, Network
Un slogan n’est pas une preuve. Préparez un marqueur qui n’apparaîtra jamais dans un trafic réel : réglez la longueur à 16, générez une fois, et traitez la chaîne entière comme un canari. Les trois étapes ci-dessous se terminent dans cet onglet. Aucun compte n’est requis.
- Ouvrez Sources dans les outils de développement, ou Afficher le code source de la page. Cherchez séparément
Math.randometgetRandomValues. Notez les fichiers et les noms de fonctions. Ne concluez pas encore. - Descendez depuis le gestionnaire de clic du bouton Générer jusqu’à ce que les caractères soient concaténés dans le résultat. Ce chemin doit appeler
crypto.getRandomValues. Si vous ne voyez queMath.random(),Date.now()ou une graine fixe, cette génération n’est pas un CSPRNG. - Passez à Network, activez Preserve log, et laissez le filtre sur All. Générez une fois de plus. Collez la chaîne entière dans la zone de recherche. La ligne de requête, la query, le corps et les charges d’analytics ne doivent pas contenir ce mot de passe.
La console prouve seulement que l’API existe
Dans Console, typeof crypto.getRandomValues === "function" qui renvoie true signifie seulement que le navigateur de ce document expose la méthode. Cela ne signifie pas que le script de la page l’a appelée. Exécuter vous-même une ligne de getRandomValues puis traiter le mot de passe de la page comme « déjà vérifié » sont deux extraits sans rapport.
Hors ligne prouve seulement que rien n’a été fabriqué à distance
Laissez la page finir de charger. Puis mettez Network sur Offline, ou coupez le réseau système, et générez à nouveau. Si un résultat apparaît encore, ce clic n’a pas dépendu d’une API en direct. C’est un signal fort pour « généré dans cet onglet ». Math.random n’a pas non plus besoin du réseau. Hors ligne est une étape à associer encore au chemin source et à la recherche du canari.
Les scripts empaquetés écrasent souvent les noms en une seule lettre. Si getRandomValues n’apparaît pas, cherchez crypto et Uint32Array, et regardez si Générer déclenche un nouveau POST métier. La minification n’est pas une dispense. Elle signifie seulement que la recherche fait un détour de plus.
Trois façons de trop lire « aléatoire »
Première : traiter le cadenas de la barre d’adresse comme la preuve que le générateur est un CSPRNG. HTTPS / TLS protège le trajet de ce navigateur jusqu’à l’extrémité TLS courante. Un serveur qui reçoit un mot de passe que vous avez envoyé en POST affiche encore un cadenas. Le cadenas ne répond pas à Math.random contre getRandomValues.
Deuxième : traiter « le paquet mentionne getRandomValues » comme la preuve que Générer a utilisé un CSPRNG. Le même fichier peut utiliser Web Crypto pour le chiffrement de fichier, un lien auto-destructeur ou une démo, tandis que le chemin du mot de passe appelle encore Math.random. L’inverse est aussi vrai : Math.random dans une publicité ou une animation ne réfute pas le chemin du mot de passe. La pile du clic est la preuve.
Troisième : traiter « il n’a pas été envoyé » comme « il est imprévisible ». Un panneau Network propre signifie seulement que, dans cette observation, le mot de passe n’a pas quitté l’onglet comme champ HTTP métier. Une extension peut encore lire le DOM. L’historique du presse-papiers du système peut encore garder la chaîne que vous avez copiée. Ce sont d’autres frontières. La qualité de l’aléa, l’envoi éventuel, et le fait qu’une copie reste quelque part après votre départ, sont trois questions distinctes.
Questions fréquentes
Si un mot de passe généré semble assez mélangé, puis-je l’utiliser comme mot de passe de connexion ?
Pas sur la seule apparence. Des caractères mélangés suggèrent seulement que le jeu peut être large. Ils ne prouvent pas que le processus est imprévisible. MDN indique que Math.random ne doit pas servir à la sécurité. Vérifiez aussi si la longueur suffit, si vous réutilisez la chaîne sur d’autres sites, et si Générer appelle crypto.getRandomValues.
Si la page génère encore un mot de passe hors ligne, l’aléa est-il sûr ?
Cela prouve seulement que ce clic n’a pas attendu une API en direct et que le serveur n’a pas fabriqué la chaîne pour vous. Math.random tourne aussi hors ligne. Après le succès Hors ligne, le chemin source doit encore montrer getRandomValues, et Network ne doit pas contenir le mot de passe que vous venez de créer.
Le code source contient à la fois Math.random et getRandomValues. Puis-je quand même l’utiliser ?
Cela dépend de la fonction appelée par le bouton Générer. Les animations et l’ordre d’affichage peuvent utiliser Math.random. Les mots de passe, jetons et clés doivent passer par getRandomValues. Ouvrez Sources et descendez depuis le gestionnaire de clic. Ne vous arrêtez pas au nombre d’occurrences de chaque nom dans le paquet.
Quelle API d’aléa FastPwd utilise-t-il, et le mot de passe est-il envoyé ?
Les caractères aléatoires et les phrases lisibles appellent tous deux crypto.getRandomValues et projettent un entier 32 bits sur le jeu de caractères avec un échantillonnage par rejet, afin d’écarter la queue du modulo. Si l’API manque, la génération est refusée. Longueur 6–128 caractères, 16 par défaut ; en dessous de 8, un avertissement indique une sécurité plus faible. Le résultat n’est pas envoyé par défaut. Prêt à l’emploi — aucun compte.
Repasser la même chaîne jetable dans un outil prêt à l’emploi
Si vous voulez une page qui écrit la frontière de l’aléa au grand jour, ouvrez le générateur de mot de passe FastPwd. Aucune inscription. Laissez le mode aléatoire à 16 caractères par défaut et générez une chaîne jetable. Reprenez la cinquième section : dans Sources, descendez depuis Générer jusqu’à getRandomValues et la boucle d’échantillonnage par rejet ; dans Network, cherchez la chaîne entière pour que la ligne de requête et le corps ratent.
Ensuite, si vous voulez aussi savoir si la chaîne ressemble à un mot de passe largement abusé, emportez une autre chaîne jetable dans le testeur de mot de passe et comparez-la à la liste publique à haute fréquence. Le mot de passe testé n’est pas envoyé. Une absence de correspondance ne prouve pas qu’il n’a jamais figuré sur le web ; une correspondance dit seulement qu’il est trop courant. C’est la frontière de la liste de mots faibles, pas celle du CSPRNG. Ne collez pas un mot de passe que vous allez activer dans un testeur en ligne.
Avant le prochain onglet générateur inconnu, retenez trois faits. Sembler mélangé ne prouve pas l’imprévisibilité. Math.random peut réussir des suites statistiques et n’être toujours pas un CSPRNG. Voir getRandomValues dans le paquet ne suffit pas : il faut le chemin que vous avez cliqué, et un canari qui confirme que le mot de passe n’est pas parti comme donnée métier. Les observations que vous pouvez répéter méritent d’entrer dans votre propre liste de relecture.
Pour aller plus loin
« Chiffrement local, rien n’est envoyé » n’est pas une preuve non plus — cherchez dans Network avec un canari dans Vérifier que le chiffrement local n’envoie pas le texte en clair. Une liste locale de mots faibles peut attraper des mots trop courants et n’est pas une recherche sur tout le web ; voir Ce qu’une liste locale de mots de passe faibles peut prouver, et pourquoi ce n’est pas une recherche sur tout le web. Si vous devez ensuite remettre le mot de passe une fois à un collègue, pourquoi la partie après # reste hors de HTTP par défaut est dans Pourquoi un fragment d’URL peut porter une clé, et quand cette protection lâche.