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
isDictWord()addDictionaryAttacks()applyHighFidelityCalibration()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 :
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.
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 :- Le mot de passe #1 ("123456") représentait 290 731 comptes (2%)
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 minutesL'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 :
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 :
Pour une wordlist de 14 milliards d'entrées (taille HIBP) sur 12× RTX 4090 :
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 :
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éfenseursHIBP est la référence mondiale car :
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 :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 :
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 microsecondesMê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 :
8.2 Normalisation Unicode (NFC)
La normalisation normalize("NFC") garantit que les caractères accentués sont traités de façon cohérente :
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 :
hybridVuln = true → 1000 mutations testées sur la base dictionnaire9. 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.
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 :
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 :
10.4 Exemple numérique complet
Pour le mot de passe "soleil" (mot français courant, détecté comme dictWord) :
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
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;
common : la probabilité est quasi-certaine. Le mot de passe est dans HIBP → l'attaque dictionnaire s'applique avec une confiance maximale.dictWord seul : haute probabilité. Un mot de dictionnaire est vulnerable, mais un attaquant doit disposer de la bonne wordlist linguistique.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.99evidenceBoost = min(0.12, 2 × 0.04) = 0.08confidence = min(1, max(0.99, 0.6) + 0.08) = min(1, 1.07) = 1.0Confiance 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 :
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 dictL'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ériquesUn 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 IPLe 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 :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
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.common ? 0.15 (niveau top 10k)Sources industrielles
Have I Been Pwned. (2024). Pwned Passwords. https://haveibeenpwned.com/PasswordsSources secondaires (contexte)
Troy Hunt. (2013). Introducing 306 Million Freely Downloadable Pwned Passwords. troyhunt.com.Sources web citées dans l'application Time2Crack
IEEE Xplore (référence dictionnaire). https://ieeexplore.ieee.org/document/6234435descDict (app.js) pour l'ordre de grandeur de couverture des top dictionnaires.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)*