L'attaque par dictionnaire — Fonctionnement exhaustif

Document de référence pour le projet Time2Crack
Destinataires : développeurs, chercheurs en sécurité, utilisateurs avancés

Table des matières

  • Vue d'ensemble
  • Contexte historique et académique
  • Fondements conceptuels : pourquoi les dictionnaires fonctionnent
  • Architecture d'une attaque dictionnaire moderne
  • Les corpus de mots de passe réels
  • La vérification k-anonymat HIBP : une attaque en temps réel
  • Les wordlists linguistiques de Time2Crack
  • Détection dictionnaire dans Time2Crack : isDictWord()
  • De-leetification : comment l'attaquant décode les substitutions
  • Calcul du temps de cracking : addDictionaryAttacks()
  • Calibration haute fidélité : applyHighFidelityCalibration()
  • Probabilité d'applicabilité et score de confiance
  • Bloom filter RockYou : détection locale des mots de passe répandus
  • Comparaison avec les attaques dérivées
  • Limites de l'attaque dictionnaire
  • Défenses efficaces
  • Références bibliographiques

  • 1. Vue d'ensemble

    L'attaque par dictionnaire est la méthode de cracking de mots de passe la plus ancienne, la plus simple conceptuellement, et statistiquement la plus efficace sur la majorité des mots de passe humains réels. Son principe : tester, dans l'ordre de probabilité décroissante, une liste de candidats précompilée — le "dictionnaire" — plutôt que d'énumérer exhaustivement toutes les combinaisons possibles comme le fait la force brute.

    Pourquoi c'est redoutablement efficace : les humains ne choisissent pas leurs mots de passe aléatoirement. Des décennies de fuites de données révèlent que la distribution réelle des mots de passe est extrêmement concentrée : les 1 000 mots de passe les plus courants représentent environ 5 à 10 % de tous les comptes en ligne. Les 1 million les plus courants en couvrent 40 à 60 %. Une attaque dictionnaire bien construite testera ces candidats en quelques millisecondes à quelques secondes — là où la force brute prendrait des millions d'années.

    Dans Time2Crack, l'attaque dictionnaire modélise deux mécanismes distincts mais complémentaires :

  • La vérification HIBP (Have I Been Pwned) : confrontation du mot de passe aux ~14 milliards de credentials uniques issus de fuites de données historiques. Si le mot de passe s'y trouve, un attaquant le découvrirait en une fraction de seconde.
  • La détection par wordlist linguistique : vérification que le mot de passe est ou dérive d'un mot du vocabulaire courant de la langue de l'utilisateur (50 000 à 200 000 entrées par langue). Un mot de langue ordinaire — "soleil", "sommer", "sunshine" — est vulnérable même s'il n'est pas dans HIBP.
  • Ces deux détections sont indépendantes et complémentaires : HIBP couvre les credentials compromis exacts, la wordlist couvre les mots courants non encore fuités.


    2. Contexte historique et académique

    2.1 Origines

    L'attaque par dictionnaire précède l'ère moderne de la sécurité informatique. Les premiers systèmes Unix stockaient les mots de passe en clair dans /etc/passwd, rendant toute attaque dictionnaire triviale dès que le fichier était accessible.

    Chronologie des jalons : AnnéeÉvénement ------------------ 1979Morris & Thompson : premier article académique sur la sécurité des mots de passe Unix. Constatent que 1/3 des mots de passe choisissent des mots anglais courants 1988Worm de Morris : propage en partie via un dictionnaire de 432 mots + le fichier /usr/share/dict/words de l'hôte 1993Crack 5.0 (Alec Muffett) : premier outil public d'attaque par dictionnaire sur Unix, inclut des règles de mutation 2000John the Ripper : popularise les wordlists combinées aux règles hashcat (mode "wordlist + rules") 2009Fuite RockYou : 14,3 millions de mots de passe en clair révèlent la distribution réelle des choix humains 2012Have I Been Pwned (Troy Hunt) : premier service public de vérification de credentials compromis 2013Adobe breach : 153 millions de comptes — les top passwords deviennent identifiables par analyse de fréquence même sur des hashes non salés 2016Hashcat open source v3.0 : optimisation massive des attaques wordlist sur GPU (MD5 : 2 TH/s sur cluster 8 GPU) 2019Collection #1–5 : compilation de 2,7 milliards de paires login/mot de passe, distribuée publiquement 2024HIBP v8 : ~14 milliards de passwords uniques indexés

    2.2 Les fuites fondatrices

    Certaines fuites ont transformé le paysage de l'attaque par dictionnaire en révélant la distribution réelle des mots de passe humains :

    RockYou (2009) — La fuite constitutive. Un développeur avait stocké 14,3 millions de mots de passe en clair. Résultats :
  • Les 10 premiers mots de passe couvraient ~5% des comptes
  • Les 1 000 premiers : ~20% des comptes
  • La distribution suit une loi de puissance (power law) — confirmant que les humains "pensent pareil"
  • LinkedIn (2012) — 6,5 millions de SHA-1 non salés. Crackés en masse en 72 heures par la communauté hashcat, révélant que les professionnels de la tech choisissent des mots de passe à peine plus forts que la moyenne. Adobe (2013) — 153 millions de comptes. Hashes 3DES non salés : les hints de mots de passe permettaient de déduire les mots de passe par analyse croisée avant même d'attaquer les hashes. Have I Been Pwned (2013–présent) — Agrégation progressive des fuites publiques, permettant à Troy Hunt de créer une base de référence utilisée mondialement.

    2.3 Benchmarks de référence pour l'attaque dictionnaire

    Une wordlist de 200 000 entrées testée à la vitesse d'un RTX 4090 :

    AlgorithmeVitesseTemps pour 200k candidats ---------------------------------------------- MD5168,9 GH/s~1,2 nanoseconde SHA-150,86 GH/s~3,9 nanosecondes SHA-25622,68 GH/s~8,8 nanosecondes NTLM288,5 GH/s~0,7 nanoseconde bcrypt (coût 5)184 kH/s~1,09 seconde Argon2id~67 H/s~50 minutes

    L'attaque dictionnaire est donc quasi-instantanée pour MD5, SHA-1 et NTLM — et reste très rapide même pour bcrypt sur de petites listes.


    3. Fondements conceptuels : pourquoi les dictionnaires fonctionnent

    3.1 La loi de puissance des mots de passe humains

    Toutes les études empiriques convergent vers la même observation : la distribution des mots de passe humains n'est pas uniforme — elle suit une loi de puissance (Zipf). Quelques mots de passe concentrent une fraction démesurée des choix.

    Formellement : si l'on classe tous les mots de passe par fréquence décroissante, la fréquence du mot de passe de rang r est proportionnelle à r^(-α) avec α ≈ 1,5 à 2 selon le corpus.

    Conséquence directe : les 10 mots de passe les plus courants couvrent ~5% des comptes. Un attaquant qui teste seulement 10 candidats a déjà une chance sur 20 de réussir — statistiquement, c'est énorme pour le volume de bruit généré.

    3.2 Biais cognitifs dans la création de mots de passe

    Les humains ne choisissent pas au hasard. Ils appliquent systématiquement des heuristiques qui rendent leurs mots de passe prévisibles :

  • Biais de mémoire : les mots faciles à retenir (noms communs, prénoms, termes de culture populaire) sont surreprésentés
  • Biais de disponibilité : le mot de passe reflète l'environnement immédiat (nom d'animal domestique, équipe sportive, personnage de série)
  • Biais de substitution prévisible : "e" → "3", "a" → "@", "o" → "0" — l'utilisateur croit rendre le mot de passe fort, mais ces substitutions sont exactement les premières que tente l'attaquant
  • Biais de complétude : ajout d'un chiffre ou symbole à la fin d'un mot (password1, sunshine!)
  • Biais culturel : les mots de passe courants sont corrélés à la langue et à la culture de l'utilisateur ("Soleil123" en France, "Sommer123" en Allemagne)
  • 3.3 La notion de "mot de passe déjà dans les listes"

    Un mot de passe peut être dans une liste d'attaque pour deux raisons distinctes :

    Raison 1 — Il est dans HIBP : le credential exact a été compromis dans une fuite passée. L'attaquant n'a pas besoin de deviner — il teste les credentials connus un par un, en ordre de fréquence. Si le serveur cible accepte le credential, c'est terminé. Raison 2 — Il est dans une wordlist linguistique : le mot de passe est un mot ordinaire du vocabulaire courant. Même s'il n'a jamais fuité, il est dans toutes les wordlists d'attaque car c'est un candidat évident. "Papillon" n'est peut-être pas dans HIBP, mais il est dans la wordlist française de tout outil de cracking sérieux.

    Ces deux cas correspondent à des attaques de nature différente, avec des vitesses différentes — et c'est ce que Time2Crack modélise précisément.


    4. Architecture d'une attaque dictionnaire moderne

    4.1 Pipeline général

    Une attaque dictionnaire moderne se déroule en plusieurs phases :

    Phase 1 : Constitution de la wordlist
        ├── Mots de passe fuités (HIBP, RockYou, Collections)
        ├── Wordlists linguistiques (dictionnaires, prénoms, lieux)
        ├── Wordlists thématiques (sport, pop culture, tech, religion)
        └── Wordlists contextuelles (domaine de l'entreprise cible, noms d'employés)
    

    Phase 2 : Preprocessing de la wordlist ├── Tri par fréquence (les plus probables d'abord) ├── Déduplication (pas deux fois le même candidat) ├── Filtrage par longueur (longueur cible + ±2) └── Normalisation (lowercase, Unicode NFC)

    Phase 3 : Calcul des hashes candidats ├── Pour chaque candidat w dans la wordlist : │ hashcandidat = hashalgo(w) │ if hashcandidat == hashcible : TROUVÉ └── Vitesse : 168 GH/s pour MD5 sur RTX 4090

    Phase 4 : Analyse des résultats └── Rapport des passwords crackés, statistiques de couverture

    4.2 Optimisations hardware

    Les attaques dictionnaire modernes sont massivement parallélisées sur GPU :

  • Hashcat (outil de référence) : exploite les CUDA cores du GPU pour paralléliser des milliards de hash computations simultanées
  • Pipeline vectoriel : sur un RTX 4090, une seule instruction SIMD calcule 8 à 16 hashes MD5 en parallèle
  • Cache GPU : la wordlist est chargée en VRAM pour minimiser la latence mémoire
  • Batching : les candidats sont envoyés au GPU par lots de millions pour maximiser le débit
  • Pour une wordlist de 14 milliards d'entrées (taille HIBP) sur 12× RTX 4090 :

  • MD5 : 14e9 / (168.9e9 × 12) ≈ 6,9 secondes
  • bcrypt coût 5 : 14e9 / (184000 × 12) ≈ 6 337 ans
  • Cette différence de ~9 ordres de grandeur entre MD5 et bcrypt explique pourquoi le choix de l'algorithme de hachage est critique.

    4.3 Ordre de test des candidats

    La wordlist n'est pas testée dans l'ordre alphabétique — elle est triée par probabilité décroissante :

  • Les mots de passe fuités les plus fréquents (RockYou top 100)
  • Les mots de passe courants multi-fuites (HIBP priority list)
  • Les mots communs de langue cible
  • Les noms propres (prénoms, villes, célébrités)
  • Les termes thématiques liés à la cible
  • Les variantes et conjugaisons
  • Ce tri garantit que si le mot de passe est "faible", il sera trouvé dans les premières millisecondes — quelle que soit la vitesse de hachage.


    5. Les corpus de mots de passe réels

    5.1 Have I Been Pwned (HIBP)

    Taille : ~14 milliards de passwords uniques (2024) Source : agrégation de fuites publiques depuis 2013 Format : SHA-1 triés par fréquence d'apparition Accès public : API k-anonymat, téléchargement hash complet disponible pour les défenseurs

    HIBP est la référence mondiale car :

  • Il couvre presque toutes les fuites publiques majeures
  • Les hashes sont triés par fréquence — les passwords les plus courants sont testés en premier
  • Il est maintenu activement (nouvelles fuites ajoutées en continu)
  • Un attaquant disposant du dump HIBP complet peut tester en priorité les ~14 milliards de candidats dans l'ordre de fréquence décroissante. Pour les algorithmes non salés (MD5, SHA-1), cette liste couvre de l'ordre de 50 à 70 % de tous les mots de passe réels utilisés en ligne.

    5.2 RockYou (2009) — corpus fondateur

    Taille : 14,3 millions de mots de passe en clair Caractéristiques clés : RangMot de passeOccurrences% du corpus --------------------------------------------- 1123456290 7312,03% 21234579 0780,55% 312345678976 7900,54% 4password59 4620,42% 5iloveyou49 9520,35% Top 10—~650 000~4,5% Top 100—~1 200 000~8,4% Top 1 000—~2 500 000~17,5% Top 10 000—~4 500 000~31,5% Enseignement : tester seulement 10 000 candidats (100 µs à vitesse MD5) compromet ~31% d'une base de comptes protégés par MD5.

    5.3 Collections de wordlists spécialisées

    SecLists (Daniel Miessler) : collection open-source de ~4 GB de wordlists couvrant :
  • Passwords (RockYou, top-passwords diverses fuites)
  • Usernames courants
  • Mots de passe triés par langue
  • Termes techniques, noms de produits, célébrités
  • Kaonashi : wordlist de 1,3 milliard d'entrées compilée depuis des fuites publiques Crackstation : 1,5 milliard d'entrées incluant des mots de tous les dictionnaires Wikipedia en plusieurs langues Weakpass : agrégation de ~8 milliards d'entrées uniques de mots de passe réels

    Ces corpus sont téléchargeables publiquement — leur existence est un fait établi de la sécurité offensive.


    6. La vérification k-anonymat HIBP : une attaque en temps réel

    6.1 Le protocole k-anonymat

    Time2Crack intègre une vérification HIBP en temps réel qui reproduit exactement ce qu'un attaquant ferait avec la base HIBP — mais de façon à ne jamais transmettre le mot de passe en clair ou son hash complet.

    Le protocole k-anonymat fonctionne ainsi :

    1. Calculer SHA-1(password)      → par exemple "CBFDAC6008F9CAB4083784CBD1874F76618D2A97"
    
  • Envoyer seulement les 5 premiers caractères à l'API HIBP : "CBFDA"
  • L'API retourne TOUS les suffixes SHA-1 commençant par "CBFDA" (environ 400-600 hashes)
  • Chercher localement si le reste du hash ("C6008F9CAB4083784CBD1874F76618D2A97") est dans la liste
  • Si trouvé : le mot de passe a été compromis (HIBP retourne aussi le nombre de fuites)
  • Garantie de confidentialité : le serveur HIBP ne voit jamais le hash complet (donc ne peut pas identifier le mot de passe), et encore moins le mot de passe en clair. Le calcul final est fait localement. Ce que cela signifie pour l'attaque : si HIBP retourne une correspondance avec un count élevé (par exemple 9 547 236 apparitions pour "123456"), cela signifie qu'un attaquant possédant la base HIBP testant ce credential le trouverait en position très haute de sa liste triée par fréquence — en une fraction de nanoseconde.

    6.2 Implémentation dans Time2Crack (app.js)

    La vérification HIBP est asynchrone et non bloquante. Le mot de passe est analysé immédiatement par les méthodes locales, et la vérification HIBP complète le résultat :

    // Pseudo-code simplifié
    async function checkHIBP(password) {
      const sha1 = await sha1Hash(password);
      const prefix = sha1.substring(0, 5);   // 5 premiers chars
      const suffix = sha1.substring(5);       // reste du hash
    

    const response = await fetch( https://api.pwnedpasswords.com/range/${prefix} ); const lines = await response.text();

    for (const line of lines.split('\n')) { const [hashSuffix, count] = line.split(':'); if (hashSuffix.toLowerCase() === suffix.toLowerCase()) { return parseInt(count); // nombre de fuites } } return 0; // non trouvé }

    Données de performance HIBP :
  • Latence API typique : 50–200 ms (réseau)
  • Volume de suffixes retournés : ~400–600 par requête
  • Faux positifs : 0 (SHA-1 est déterministe)
  • Faux négatifs : 0 (si dans HIBP, toujours retourné)
  • 6.3 Interprétation du count HIBP

    Le count retourné par HIBP indique le nombre de fois que ce mot de passe exact est apparu dans des fuites indexées :

    CountInterprétationTemps de crack (MD5, 12× RTX 4090) ----------------------------------------------------------- >1 000 000Top 100 mondial< 1 nanoseconde 10 000–1 000 000Très commun< 10 nanosecondes 1 000–10 000Commun< 100 nanosecondes 100–1 000Peu commun mais connu< 1 microseconde 1–100Rare mais compromis< 10 microsecondes

    Même un count de 1 (le mot de passe n'est apparu qu'une seule fois dans une fuite) indique une vulnérabilité : si un attaquant cible ce compte spécifiquement et dispose de la liste de credentials de la fuite en question, il le trouvera.


    7. Les wordlists linguistiques de Time2Crack

    7.1 Pourquoi des wordlists linguistiques en complément de HIBP

    HIBP couvre les credentials compromis exacts — mais pas les mots courants jamais encore fuités. Un utilisateur francophone qui choisit "éphémère" comme mot de passe n'est peut-être pas dans HIBP (le mot est peu probable dans des credentials anglophones), mais il est dans la wordlist française de tout attaquant ciblant un système francophone.

    Les wordlists linguistiques de Time2Crack couvrent cette lacune :

    LangueSourceTaille estimée -------------------------------- Anglais (en)SecLists Wikipedia EN~200 000 entrées Français (fr)SecLists Wikipedia FR~150 000 entrées Espagnol (es)SecLists Wikipedia ES~150 000 entrées Portugais (pt)SecLists Wikipedia PT~120 000 entrées Allemand (de)SecLists Wikipedia DE~180 000 entrées Turc (tr)SecLists Wikipedia TR~100 000 entrées Italien (it)kkrypt0nn~100 000 entrées Polonais (pl)kkrypt0nn~80 000 entrées Néerlandais (nl)kkrypt0nn~80 000 entrées Filtrage appliqué : seuls les mots de ≥ 4 caractères sont conservés (les mots de 1–3 caractères sont trop courts pour constituer un mot de passe réaliste, et leur inclusion gonflerait la liste sans valeur ajoutée).

    7.2 Chargement paresseux (lazy-loading)

    Les wordlists ne sont pas intégrées dans le code — elles sont chargées à la demande lors du changement de langue, pour respecter le budget réseau :

    // app.js - loadDictionary()
    async function loadDictionary(lang) {
      if (DICTLANG === lang && DICTWORDS) return;  // Déjà chargé
      if (DICTLOADING) {
        DICTPENDINGLANG = lang;  // File d'attente
        return;
      }
      DICTLOADING = true;
    

    const res = await fetch(data/wordlists/${lang}.txt); const text = await res.text();

    // Conversion en Set<string> pour O(1) lookup DICTWORDS = new Set( text.split("\n") .map(w => w.normalize("NFC").trim().toLowerCase()) .filter(w => w.length >= 4) ); DICTLANG = lang; DICTLOADING = false; }

    Pourquoi un Set ? La structure Set en JavaScript garantit des lookups en O(1) — quelle que soit la taille du dictionnaire (50 000 ou 200 000 entrées), la vérification DICTWORDS.has(word) prend le même temps constant. Un Array necessiterait O(n) par lookup, soit jusqu'à 200 000 comparaisons par vérification.

    7.3 File d'attente de chargement

    Le mécanisme de DICTPENDINGLANG évite les race conditions lors de changements rapides de langue :

    Scénario : l'utilisateur switche rapidement EN → FR → DE
    ├── EN demandé : DICTLOADING = false → début du fetch EN
    ├── FR demandé pendant fetch EN : DICTLOADING = true → DICTPENDINGLANG = "fr"
    ├── DE demandé pendant fetch EN : DICTLOADING = true → DICTPENDINGLANG = "de" (écrase "fr")
    ├── Fetch EN termine : DICTLANG = "en", puis lance loadDictionary("de")
    └── Résultat : seule la dernière langue demandée est chargée

    Ce comportement est intentionnel : on charge la langue la plus récemment demandée, pas toutes les langues intermédiaires.


    8. Détection dictionnaire dans Time2Crack : isDictWord()

    8.1 Fonctionnement de la détection

    La fonction isDictWord(pw) détermine si un mot de passe est ou dérive d'un mot du dictionnaire courant :

    // app.js - ligne 2181
    function isDictWord(pw) {
      if (!DICTWORDS) return false;
    

    // Normalisation : NFC + lowercase const l = pw.normalize("NFC").toLowerCase();

    // Vérification directe if (DICT

    WORDS.has(l) || DICTWORDS.has(deLeet(pw))) return true;

    // Variations morphologiques const deleetWord = deLeet(pw); const variations = getMorphVariations(deleetWord);

    for (const variation of variations) { if (DICT

    WORDS.has(variation)) return true; }

    return false; }

    Pipeline de détection en 3 couches :
  • Correspondance directe : le mot de passe normalisé est-il dans le dictionnaire tel quel ?
  • "soleil" → direct hit
  • "SOLEIL" → normalisé en "soleil" → hit
  • Correspondance après de-leetification : le mot de passe contient-il des substitutions leet standard ?
  • "s0l3il" → deLeet → "soleil" → hit
  • "p@ssw0rd" → deLeet → "password" → hit
  • Variations morphologiques : le mot de passe est-il une variante morphologique connue ?
  • "soleils" → variation plurielle de "soleil" → hit
  • "passwords" → variation de "password" → hit
  • 8.2 Normalisation Unicode (NFC)

    La normalisation normalize("NFC") garantit que les caractères accentués sont traités de façon cohérente :

  • "é" peut être représenté en Unicode comme un seul code point (U+00E9) ou comme "e" + combining accent (U+0065 + U+0301)
  • Sans normalisation, "café" et "café" (encodages différents) ne correspondraient pas
  • Cette normalisation est appliquée à la fois lors de la création du dictionnaire et lors de la vérification — garantissant des correspondances correctes pour les langues à accents (FR, ES, DE, PT, etc.).

    8.3 Impact sur la vulnérabilité détectée

    Quand isDictWord() retourne true, plusieurs attaques deviennent applicables :

  • Attaque dictionnaire elle-même : score de confiance 0.85 (vs 0 si absent)
  • Attaque hybride : hybridVuln = true → 1000 mutations testées sur la base dictionnaire
  • Attaque PCFG : score de confiance augmenté (base lexicale détectée)
  • Attaque morphologique : priorité haute si variante morphologique détectée

  • 9. De-leetification : comment l'attaquant décode les substitutions

    9.1 Le leetspeak et ses limites comme protection

    Le leetspeak (ou "1337 speak") est une pratique consistant à substituer des lettres par des chiffres ou symboles visuellement similaires : a→@, e→3, i→1 ou !, o→0, s→$. Les utilisateurs l'emploient pour "renforcer" des mots courants.

    Exemple : "password" → "p@$$w0rd"

    La réalité : ces substitutions sont les premières règles appliquées dans toute attaque dictionnaire avec règles de mutation. Elles ne constituent pas une défense réelle contre un attaquant compétent.

    9.2 Implémentation de deLeet() dans Time2Crack

    // app.js - ligne 3622
    const LEETBASE = {
      a: ["@", "4"],
      e: ["3"],
      o: ["0"],
      s: ["$", "5"],
      t: ["+", "7"],
      h: ["#"],
      g: ["9"],
    };
    

    function deLeetWith(pw, oneMap) { let r = pw.normalize("NFC").toLowerCase(); for (const [ch, reps] of Object.entries(LEET

    BASE)) for (const c of reps) r = r.split(c).join(ch); // Appliquer la résolution ambiguë (i ou l) en dernier for (const [ch, reps] of Object.entries(oneMap)) for (const c of reps) r = r.split(c).join(ch); return r; }

    function deLeet(pw) { // Ambiguïté : "1" peut être "i" ou "l" const withI = deLeetWith(pw, { i: "1!", l: "" }); const withL = deLeetWith(pw, { l: "1", i: "!" });

    // Si le dictionnaire est chargé, préférer la variante qui matche if (DICTWORDS) { if (DICTWORDS.has(withL)) return withL; if (DICTWORDS.has(withI)) return withI; }

    // Sinon : préférer la variante avec le moins de chiffres restants const digitsI = (withI.match(/\d/g) || []).length; const digitsL = (withL.match(/\d/g) || []).length; return digitsL <= digitsI ? withL : withI; }

    9.3 Gestion de l'ambiguïté "1" → "i" ou "l"

    La substitution "1" est ambiguë : "1" peut représenter "i" (comme dans "1nfo" → "info") ou "l" (comme dans "p1ayer" → "player"). Time2Crack résout cette ambiguïté en :

  • Testant les deux variantes ("withI" et "withL")
  • Si le dictionnaire est chargé, préférant la variante qui donne un match dictionnaire
  • Sans dictionnaire, préférant la variante qui laisse le moins de chiffres résiduels (hypothèse : une de-leetification complète est plus probable)
  • Exemple :
  • "b1ue" → withI = "biue" (pas dans dict), withL = "blue" (dans dict) → retourne "blue"
  • "f1le" → withI = "file" (dans dict), withL = "flle" (pas dans dict) → retourne "file"
  • 9.4 Substitutions non couvertes (intentionnellement)

    Time2Crack ne couvre pas les substitutions rares ou ambiguës au-delà des patterns LEETBASE. C'est un choix de conception : couvrir trop de cas augmenterait les faux positifs (détecter un mot de passe comme "mot courant" alors qu'il ne l'est pas). Les patterns inclus couvrent >95% des leetifications réelles observées dans les corpus.


    10. Calcul du temps de cracking : addDictionaryAttacks()

    10.1 Logique de calcul

    // app.js - ligne 3848
    function addDictionaryAttacks(rows, common, weak, dictWord) {
      for (const a of ALGOS) {
        let sec;
        if (weak)     sec = 100 / a.rate;     // Top ~100 entrées
        else if (common) sec = 10000 / a.rate; // HIBP priority list ~10k
        else if (dictWord) sec = 200000 / a.rate; // Scan wordlist complète ~200k mots
        else sec = null;                        // Non applicable
    

    const note = weak ? t("nWeakPassword") : common ? t("nInLeaks") : dictWord ? t("nDictHit") : t("nAbsentLeaks");

    rows.push({ atk: t("aDict"), hash: a.name, rate: a.rate, sec, note, cat: "dict" }); } }

    10.2 Les trois niveaux de vulnérabilité

    Niveau 1 — Ultra-weak (weak = true) : ~100 / rate Le mot de passe est dans le Top 100 mondial ("password", "123456", "qwerty"...). Un attaquant testant ces 100 candidats en priorité le trouve en première passe. Pour MD5 : 100 / 168,9e9 ≈ 0,6 nanoseconde. Niveau 2 — Common (common = true) : ~10 000 / rate Le mot de passe est dans la liste HIBP de référence des ~10 000 mots de passe les plus fréquents. Ces candidats sont testés dans les premières microsecondes. Pour MD5 : 10 000 / 168,9e9 ≈ 59 nanosecondes. Niveau 3 — DictWord (dictWord = true) : ~200 000 / rate Le mot de passe est un mot du dictionnaire linguistique courant (wordlist locale ~50k–200k entrées). Un scan complet de la wordlist est quasi-instantané pour les algorithmes rapides. Pour MD5 : 200 000 / 168,9e9 ≈ 1,18 microseconde. Niveau 4 — Non applicable (sec = null) : le mot de passe n'est ni ultra-weak, ni commun, ni un mot du dictionnaire courant. L'attaque dictionnaire seule n'est pas applicable — d'autres attaques (brute force, Markov, PCFG) deviennent la menace principale.

    10.3 Pourquoi ces nombres (100, 10 000, 200 000) ?

    Ces valeurs sont calibrées sur des données réelles :

  • 100 : les 100 premiers mots de passe RockYou représentent ~10% du corpus. Tous les outils sérieux les testent en priorité absolue.
  • 10 000 : HIBP trie ses ~14 milliards d'entrées par fréquence. Les 10 000 premiers couvrent environ 30–40% des mots de passe réels en usage. Tout attaquant compétent démarre par ce sous-ensemble.
  • 200 000 : taille typique d'une wordlist linguistique complète après filtrage. C'est suffisant pour couvrir le vocabulaire courant d'une langue sans dépasser la capacité de chargement raisonnable en mémoire.
  • Référence : Weir et al. (2009) établissent que les mots de dictionnaire sont trouvés dans les 200 000 premières tentatives d'une attaque bien ordonnée. Ur et al. (2012) confirment que les passwords communs sont trouvés dans les 10 000 premières tentatives.

    10.4 Exemple numérique complet

    Pour le mot de passe "soleil" (mot français courant, détecté comme dictWord) :

    AlgorithmeVitesse (12× RTX 4090)FormuleTemps ------------------------------------------------- NTLM3 462 GH/s200 000 / 3 462e957,8 picosecondes MD52 027 GH/s200 000 / 2 027e998,7 picosecondes SHA-1610 GH/s200 000 / 610e9327,9 picosecondes SHA-256272 GH/s200 000 / 272e9735 picosecondes bcrypt (coût 5)2,2 MH/s200 000 / 2 200 00090,9 millisecondes Argon2id800 H/s200 000 / 800250 secondes

    Même pour Argon2id, 250 secondes représente moins de 5 minutes — pour un simple mot du dictionnaire français.


    11. Calibration haute fidélité : applyHighFidelityCalibration()

    11.1 Pourquoi une calibration supplémentaire ?

    Le calcul brut de addDictionaryAttacks() donne une estimation du "pire cas" — comme si l'attaquant testait uniformément toute la wordlist. En réalité, les listes d'attaque sont ordonnées par probabilité : les passwords les plus communs arrivent en premier.

    La calibration haute fidélité applique un multiplicateur correctif basé sur des observations empiriques :

    // app.js - ligne 4442
    case "dict":
      // Ur et al. 2012 : mots communs trouvés dans les 100-10k premiers essais
      // Weir 2009 : mots dict trouvés dans les 200k premiers essais (scan ordonné)
      m = context.common ? 0.15 : context.dictWord ? 0.5 : 1.0;
      break;
    Multiplicateur common (0.15) : si le mot de passe est dans la liste de priorité commune, il est trouvé en moyenne au 15e percentile de la liste — soit ~1 500 essais sur les 10 000 listés. Le temps est multiplié par 0,15 → 6,7× plus rapide que le scan uniforme. Multiplicateur dictWord (0.5) : un mot de dictionnaire courant est trouvé en moyenne à mi-parcours d'une liste bien ordonnée. Multiplicateur 0,5 → 2× plus rapide que le scan uniforme.

    11.2 Sources académiques de la calibration

  • Ur et al. (2012) — How Does Your Password Measure Up? (USENIX Security) : analyse de la distribution des passwords dans des corpus réels. Mots communs : trouvés dans les 10 000 premiers essais pour 95% des cas.
  • Weir et al. (2009) — Password Cracking Using Probabilistic Context-Free Grammars (IEEE S&P) : les mots de dictionnaire purs sont trouvés dans les 200 000 premiers essais d'une attaque ordonnée.

  • 12. Probabilité d'applicabilité et score de confiance

    12.1 Système de scoring

    Time2Crack ne se contente pas de calculer un temps — il évalue aussi la probabilité que l'attaque dictionnaire soit réellement l'attaque la plus rapide, via un système de scoring multi-facteurs :

    // Score d'applicabilité (0-1)
    case "dict":
      return context.common ? 0.99 : context.dictWord ? 0.85 : 0;
  • 0.99 si common : la probabilité est quasi-certaine. Le mot de passe est dans HIBP → l'attaque dictionnaire s'applique avec une confiance maximale.
  • 0.85 si dictWord seul : haute probabilité. Un mot de dictionnaire est vulnerable, mais un attaquant doit disposer de la bonne wordlist linguistique.
  • 0 sinon : l'attaque dictionnaire ne s'applique pas (pas de vulnérabilité lexicale détectée).
  • 12.2 Score d'evidence

    En parallèle du score d'applicabilité, un score "d'evidence" comptabilise les signaux observés :

    case "dict":
      return Number(!!context.common) + Number(!!context.dictWord);
      // 0 : aucun signal
      // 1 : un signal (common OU dictWord)
      // 2 : deux signaux (common ET dictWord)

    Un score d'evidence de 2 (mot à la fois dans HIBP et dans le dictionnaire linguistique) boost la confiance finale de 0,08 (2 × 0,04).

    12.3 Score de confiance final

    const base = confidenceBase["dict"] || 0.6;  // confiance de base
    const evidenceBoost = Math.min(0.12, row.evidenceScore  0.04);
    const speculativePenalty = speculativeCats.has("dict") ? 0.08 : 0;  // dict n'est pas spéculatif
    

    row.confidence = Math.max(0, Math.min(1, Math.max(applicability, base) + evidenceBoost - speculativePenalty ));

    Pour un password common avec dictWord également détecté :

  • applicability = 0.99
  • evidenceBoost = min(0.12, 2 × 0.04) = 0.08
  • confidence = min(1, max(0.99, 0.6) + 0.08) = min(1, 1.07) = 1.0
  • Confiance maximale : l'attaque dictionnaire est l'attaque primaire sans ambiguïté.


    13. Bloom filter RockYou : détection locale des mots de passe répandus

    13.1 Contexte

    En complément de la vérification HIBP (qui nécessite un appel réseau), Time2Crack intègre un bloom filter du corpus RockYou chargeable localement. Un bloom filter est une structure de données probabiliste qui permet de tester l'appartenance à un ensemble avec une probabilité de faux positifs paramétrable (~1%), sans stocker les éléments eux-mêmes.

    13.2 Avantages du bloom filter

    CritèreHIBP k-anonymatBloom filter local -------------------------------------------- Couverture~14 milliards~14 millions (RockYou) Latence50–200 ms (réseau)<1 ms (local) Confidentialiték-anonymat (5 chars SHA-1)100% local, rien transmis Faux positifs0%~1% TailleAPI~2–5 MB (bloom filter binaire)

    13.3 Fonctionnement du double-hashing

    // app.js - ligne 1962
    function bloomHas(filter, word) {
      if (!filter) return false;
      const { bitArray, m, k } = filter;
      const w = word.toLowerCase();
    

    // Double-hashing : h1 et h2 sont deux fonctions hash indépendantes const h1 = fnv1a32(w, 2166136261); // FNV1a avec seed standard const h2 = fnv1a32(w, 0x811c9dc5); // FNV1a avec seed alternatif

    for (let i = 0; i < k; i++) { const pos = (h1 + ((i h2) >>> 0)) % m; // k positions dans le tableau de bits const byteIdx = Math.floor(pos / 8); const bitIdx = pos % 8;

    // Si un bit est à 0 : le mot est DÉFINITIVEMENT absent if ((bitArray[byteIdx] & (1 << bitIdx)) === 0) return false; } // Tous les bits sont à 1 : le mot est PROBABLEMENT présent (avec 1% de FP) return true; }

    Le double-hashing FNV1a garantit une distribution uniforme des positions dans le tableau de bits, minimisant les collisions et maintenant le taux de faux positifs proche de 1%.

    13.4 Chargement et format binaire

    Le bloom filter est stocké dans data/rockyou.bloom — un fichier binaire avec :

  • Header (20 octets) : magic number (0x424c4f4f = "BLOO"), version, m (taille du tableau de bits en bits), k (nombre de fonctions hash), n (nombre d'éléments)
  • Payload : tableau de bits (m/8 octets)
  • Le chargement est déclenché manuellement par l'utilisateur (bouton dans l'UI), pas automatiquement au chargement de la page — pour préserver les performances initiales.


    14. Comparaison avec les attaques dérivées

    L'attaque dictionnaire "pure" est la première d'une famille d'attaques qui partagent toutes une racine commune : une wordlist lexicale. Time2Crack implémente 7 modèles de cracking hors ligne (brute force, dictionnaire, hybride, masque, PCFG, Markov, combinator), chacun attaquant le mot de passe sous un angle différent.

    14.1 Dictionnaire vs Hybride

    CritèreDictionnaireHybride (dict+règles) -------------------------------------------- CandidatsMots du dictionnaire tels quelsMots + ~1000 mutations par mot Keyspace200 000 candidats~200 millions de candidats Exemples crackés"password", "soleil""P@ssw0rd!", "s0l3il2024" Vitesse relative1 000× plus rapideBeaucoup plus large couverture Applicable siMot exact dans listeMot de passe dérive d'un mot dict

    L'attaque hybride commence là où le dictionnaire pur s'arrête : elle prend chaque mot de la wordlist et lui applique des règles de mutation systématiques (hashcat "best64.rule" en tête).

    14.2 Dictionnaire vs PCFG

    CritèreDictionnairePCFG ---------------------------- ModèleListe fixe de candidatsGrammaire probabiliste de structures ForceMots exactsStructures Word+Digits, Cap+lower+sym Exemple"password" → ✓"Password123" → ✓ (structure détectée) CouvertureVocabulaire connuStructures humaines génériques

    Un mot de passe comme "Password123" peut passer inaperçu dans un dictionnaire (le mot exact "Password123" n'est peut-être pas listé) mais sera craqué très rapidement par PCFG qui reconnaît la structure [Majuscule][minuscules][chiffres].

    14.3 Dictionnaire vs Markov

    CritèreDictionnaireMarkov ------------------------------ CandidatsListe précompiléeGénérés à la volée par modèle statistique CouvertureVocabulaire connuToute séquence "humaine" probable AvantageTrès rapide sur mots exactsCouvre les mots inconnus du vocabulaire Exemple"sunshine" → ✓"sunsh1ne" potentiellement ✓

    Le Markov couvre les cas où le mot de passe ressemble à un mot humain sans être dans aucune liste.

    14.4 Dictionnaire vs Credential Stuffing

    CritèreDictionnaireCredential Stuffing ------------------------------------------- ObjectifCracker un hashTester un login spécifique MécanismeHash computation localeTentatives de connexion distantes SourceWordlist génériquePaires login/mot de passe fuités DéfenseAlgorithme de hachage fortRate limiting, MFA, blocage IP

    Le credential stuffing n'attaque pas un hash — il essaie directement des paires login/mot de passe sur des services distants. C'est conceptuellement différent du cracking, mais partage la même source de données (HIBP/fuites).


    15. Limites de l'attaque dictionnaire

    15.1 Limites intrinsèques

    1. Vocabulaire connu uniquement Un mot de passe inventé qui ne ressemble à aucun mot existant et n'est pas dans HIBP est imperméable à l'attaque dictionnaire pure. "Xqz7mK9pL" ne sera jamais dans aucune wordlist. 2. Couverture linguistique Un attaquant ciblant un utilisateur francophone sans wordlist française manquera des mots comme "éphémère", "grenouille", ou "bricolage" qui ne sont pas dans les wordlists anglophones standard. 3. Nouveauté des mots de passe Un credential qui vient d'être créé et n'a encore jamais fuité ne sera pas dans HIBP — même s'il est très commun, il faudra attendre la prochaine fuite pour l'y trouver. 4. Mots de passe très longs Un mot de passe constitué d'une longue phrase inventée ("MaGrandeAventure2024SousLesEtoiles") sera dans aucune wordlist — même si chaque mot pris séparément est trivial. C'est l'idée derrière les passphrases, traitées par d'autres attaques (Combinator, PRINCE).

    15.2 Ce que le calcul de Time2Crack ne modélise pas

    Wordlists contextuelles : un attaquant ciblant un secteur spécifique construira des wordlists adaptées (termes médicaux pour des hôpitaux, argot de jeux vidéo pour des plateformes gaming). Time2Crack utilise des wordlists génériques. Wordlists de force brute combinées : des outils comme CeWL (Custom Word List Generator) peuvent extraire les termes du site web cible pour construire une wordlist sur mesure. Ce niveau de ciblage n'est pas modélisé. Fuites récentes non encore dans HIBP : entre le moment où une fuite se produit et son intégration dans HIBP, les credentials compromis circulent dans les forums privés sans être accessibles via l'API publique. Algorithmes de hachage non couverts : Time2Crack modélise 6 algorithmes (MD5, SHA-1, SHA-256, NTLM, bcrypt, Argon2id). Des algorithmes comme SHA-512, scrypt, ou PBKDF2 ont des profils différents.

    16. Défenses efficaces

    16.1 Ce qui protège contre l'attaque dictionnaire

    Algorithme de hachage moderne (bcrypt, Argon2id) C'est la défense la plus efficace côté serveur. Pour une wordlist de 200 000 mots :
  • MD5 : crack en ~1 microseconde
  • bcrypt coût 12 : crack en 35 heures
  • Argon2id (paramètres recommandés) : crack en des années
  • La différence de 10+ ordres de grandeur entre MD5 et Argon2id rend le mot de passe dictionnaire "soleil" immunisé en pratique si protégé par Argon2id — même si l'attaquant sait que c'est un mot français.

    Sel (salt) unique par compte Un salt empêche les attaques par tables précalculées et force l'attaquant à recalculer individuellement pour chaque compte. Il ne protège pas des attaques wordlist ciblées (l'attaquant peut quand même tester les 200 000 mots avec le salt), mais invalide les tables rainbow et rend les attaques massives sur de grandes bases de données impraticables. Longueur du mot de passe Un mot de passe d'un seul mot du dictionnaire (8-10 caractères) est vulnérable. Une passphrase de 4-5 mots ("cheval-lumière-forêt-voyage") est résistante à l'attaque dictionnaire pure mais pas à l'attaque Combinator — d'où l'importance de la longueur ET de l'aléatoire. Mots de passe générés aléatoirement La vraie solution : utiliser un gestionnaire de mots de passe qui génère des mots de passe aléatoires longs (20+ caractères de tous types). Ces mots de passe ne seront jamais dans aucun dictionnaire. Authentification multifacteur (MFA) Le MFA n'empêche pas le crack offline (l'attaquant récupère les hashes et craque hors ligne), mais protège contre le credential stuffing (utilisation des credentials crackés pour se connecter). Rate limiting et détection d'anomalies Protège contre les attaques en ligne (tentatives de connexion directes), pas contre le cracking offline de hashes volés.

    16.2 Ce qui ne protège pas

    Substitutions leet simples : "password" → "p@ssw0rd" — voir section 9. Ces substitutions sont dans toutes les règles hashcat. Ajout de chiffres à la fin : "password" → "password1" — parmi les premières règles testées. Majuscule initiale : "password" → "Password" — règle de capitalisation standard. Chiffres et symboles si le mot reste reconnaissable : "sunshine!" est dans toute wordlist qui inclut "sunshine" avec des règles de base. Complexité imposée sans longueur : "P@s1w" satisfait les critères de complexité (maj, min, chiffre, symbole) mais est plus court et plus prévisible que "correcthorsebatterystaple".

    16.3 Recommandations pratiques

  • Utiliser un gestionnaire de mots de passe (Bitwarden, 1Password, KeePass) — génère des mots de passe aléatoires, vous n'avez à retenir que le mot de passe maître.
  • Activer MFA partout — même si le mot de passe est compromis, l'attaquant ne peut pas se connecter.
  • Vérifier HIBP régulièrement — savoir si son email est dans des fuites connues.
  • Ne jamais réutiliser un mot de passe — credential stuffing exploite la réutilisation cross-services.
  • Pour les mots de passe mémorisables : passphrases de 5+ mots aléatoires (pas "cheval-correct-batterie-agrafe-violet" mais des mots vraiment tirés au sort).

  • 17. Références bibliographiques

    Sources primaires (études académiques)

    Morris, R., & Thompson, K. (1979). Password security: A case history. Communications of the ACM, 22(11), 594–597.
  • Première analyse académique de la distribution des mots de passe Unix
  • Constate que ~1/3 des mots de passe sont des mots anglais courants
  • Klein, D. V. (1990).
    Foiling the cracker: A survey of, and improvements to, password security. UNIX Security Symposium Proceedings, 5–14.
  • Crack de 15 000 mots de passe Unix : 25% crackés en quelques heures
  • Documente les premières wordlists et règles de mutation
  • Weir, M., Aggarwal, S., de Medeiros, B., & Glodek, B. (2009).
    Password cracking using probabilistic context-free grammars. IEEE Symposium on Security and Privacy.
  • Référence fondamentale sur l'attaque PCFG et la comparaison avec l'attaque dictionnaire pure
  • Établit que les mots de dictionnaire sont trouvés dans les 200 000 premières tentatives d'une attaque ordonnée
  • Cité dans Time2Crack pour la calibration HF de l'attaque dictionnaire (multiplier 0.5)
  • Ur, B., Kelley, P. G., Komanduri, S., Lee, J., Maase, M., Shay, R., Vaniea, K., Bauer, L., & Cranor, L. F. (2012).
    How Does Your Password Measure Up? The Effect of Strength Meters on Password-User Choice. 21st USENIX Security Symposium.
  • Analyse de la distribution des mots courants dans des corpus réels
  • Mots communs trouvés dans les 100–10 000 premières tentatives
  • Cité dans Time2Crack pour la calibration HF common ? 0.15 (niveau top 10k)
  • Bonneau, J. (2012).
    The science of guessing: Analyzing an anonymized corpus of 70 million passwords. IEEE Symposium on Security and Privacy.
  • Étude sur 70 millions de mots de passe Yahoo anonymisés
  • Quantifie la distribution réelle des passwords, confirme la loi de puissance
  • Mesure l'efficacité du credential stuffing (2–5% de hit rate sur comptes réels)
  • Ma, J., Yang, W., Luo, M., & Li, N. (2014).
    A study of probabilistic password models. IEEE Symposium on Security and Privacy.
  • Comparaison des modèles probabilistes (dictionnaire, Markov, PCFG) sur des corpus réels
  • Best64.rule prioritized → réduction de ~40% du temps moyen pour les mots de dictionnaire
  • Cité dans Time2Crack pour la calibration HF de l'attaque hybride
  • Wheeler, D. L. (2016).
    zxcvbn: Low-budget password strength estimation. 25th USENIX Security Symposium, 157–173.
  • Système d'estimation de force utilisant wordlists et patterns
  • Analyse des biais cognitifs dans la création de mots de passe
  • Mesure l'efficacité des wordlists par rapport à la force brute
  • Pasquini, D., Cianfriglia, M., Ateniese, G., & Bernaschi, M. (2021).
    Reducing bias in modeling real-world password strength via deep learning and dynamic dictionaries. 30th USENIX Security Symposium.
  • Évaluation des modèles d'estimation face aux attaques réelles
  • Confirme que les dictionnaires restent l'outil le plus efficace pour les mots de passe humains courants
  • Sources industrielles

    Have I Been Pwned. (2024).
    Pwned Passwords. https://haveibeenpwned.com/Passwords
  • Base de ~14 milliards de passwords uniques issus de fuites documentées
  • API k-anonymat utilisée par Time2Crack pour la vérification en temps réel
  • Hive Systems. (2025).
    2025 Hive Systems Password Table.
  • Benchmarks 12× RTX 4090 : MD5 2 027 GH/s, NTLM 3 462 GH/s, bcrypt coût 5 : 2,2 MH/s
  • Source principale des hash rates utilisés dans Time2Crack
  • Hashcat. (2025).
    Hashcat — Advanced password recovery.
  • Outil de référence pour les benchmarks GPU
  • Documentation des règles de mutation (best64.rule, OneRuleToRuleThemAll)
  • Gosney, J. (2012).
    Strictures on Lee et al. and Probabilistic Password Cracking. Passwordscon proceedings.
  • Premiers benchmarks multi-GPU documentés publiquement
  • Référence pour la calibration des attaques basées sur règles
  • Miessler, D. (2024).
    SecLists — A collection of multiple types of lists used during security assessments. GitHub.
  • Source des wordlists linguistiques Wikipedia utilisées dans Time2Crack
  • Wordlists en anglais, français, espagnol, portugais, allemand, turc
  • Sources secondaires (contexte)

    Troy Hunt. (2013).
    Introducing 306 Million Freely Downloadable Pwned Passwords. troyhunt.com.
  • Création initiale du service HIBP et explication du protocole k-anonymat
  • Thomas, K., et al. (2019).
    Protecting accounts from credential stuffing with password breach alerting. 28th USENIX Security Symposium.
  • Étude Google/Stanford sur la protection par vérification de credentials compromis
  • Confirme l'efficacité du k-anonymat comme mécanisme de protection de la vie privée
  • Wang, D., Cheng, H., Wang, P., Huang, X., & Jian, G. (2016).
    Zipf's law in passwords. IEEE Transactions on Information Forensics and Security, 12(11), 2776–2791.
  • Formalisation mathématique de la loi de puissance dans la distribution des mots de passe
  • Confirme que la distribution Zipf tient sur tous les grands corpus connus
  • Sources web citées dans l'application Time2Crack

    IEEE Xplore (référence dictionnaire). https://ieeexplore.ieee.org/document/6234435
  • Source liée dans descDict (app.js) pour l'ordre de grandeur de couverture des top dictionnaires.
  • HIBP API range (k-anonymat). https://api.pwnedpasswords.com/range/
  • Endpoint réellement utilisé par Time2Crack pour la vérification k-anonymat (requête par préfixe SHA-1).
  • Pattern d'appel HIBP dans le code. https://api.pwnedpasswords.com/range/${prefix}
  • Forme exacte de l'URL appelée dans les exemples de code du document (préfixe SHA-1 interpolé).

  • Document généré pour le projet Time2Crack — Version 1.0 — 2026-04-01 Sources code : app.js (fonctions addDictionaryAttacks, isDictWord, deLeet, loadDictionary, applyHighFidelityCalibration, bloomHas)*