Déchiffrer les mots de passe Firefox : NSS, key4.db et logins.json
2026-06-17 · 4 min
Chrome s'appuie sur le magasin de clés du système d'exploitation — DPAPI sous Windows, le Keychain sous macOS. Firefox fait sa propre cuisine : les mots de passe enregistrés sont protégés par NSS (Network Security Services), la même bibliothèque cryptographique que Firefox utilise pour TLS. Rien n'est lié à votre compte Windows, ce qui rend les mots de passe Firefox plus récupérables hors ligne que ceux de Chrome — il vous suffit de deux fichiers et (parfois) du mot de passe principal.
Les deux fichiers
| Fichier | Contenu | Format |
|---|---|---|
logins.json | les noms d'utilisateur et mots de passe chiffrés | JSON |
key4.db | la clé qui les déchiffre | SQLite |
Les deux se trouvent à la racine du profil (voir
emplacements des fichiers Firefox).
Les cookies, c'est une autre histoire : cookies.sqlite est en clair, il
n'a donc besoin d'aucune clé (contrairement aux
cookies Chrome).
logins.json est un tableau d'entrées :
{
"logins": [
{
"hostname": "https://example.com",
"encryptedUsername": "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECC4...",
"encryptedPassword": "MEIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECF...",
"timeCreated": 1700000000000,
"timeLastUsed": 1701234567890
}
]
}
Chaque valeur encrypted* est le base64 d'une structure ASN.1 : un
identifiant de clé, un chiffrement + IV, et le texte chiffré. Le chiffrement
des entrées elles-mêmes est toujours 3DES-CBC — mais la clé provient de
key4.db.
À l'intérieur de key4.db
key4.db (SQLite ; il a remplacé l'ancien key3.db au format Berkeley-DB
aux alentours de 2018) comporte deux tables qui comptent :
metadata— la ligneid = 'password'porteitem1(le sel global) etitem2(un blob de vérification du mot de passe).nssPrivate— la ligne dont lea102vaut l'identifiant bien connuf8000000000000000000000000000001portea11, la clé chiffrée.
item2 et a11 sont tous deux encapsulés de la même manière, par une clé
dérivée du sel global et de votre mot de passe principal (vide par
défaut). L'encapsulage utilisé est annoncé par l'OID de l'algorithme dans
l'ASN.1 :
| OID | Schéma |
|---|---|
1.2.840.113549.1.12.5.1.3 | pbeWithSha1And3KeyTripleDES-CBC (3DES historique) |
1.2.840.113549.1.5.13 | PBES2 → PBKDF2-HMAC-SHA256 + AES-256-CBC (moderne) |
Dérivation de la clé
PBE 3DES historique. Une cascade SHA-1/HMAC-SHA1 sur les sels :
hp = SHA1(globalSalt || primaryPassword)
chp = SHA1(hp || entrySalt)
k1 = HMAC-SHA1(chp, pes || entrySalt) # pes = entrySalt padded to 20 bytes
tk = HMAC-SHA1(chp, pes)
k2 = HMAC-SHA1(chp, tk || entrySalt)
k = k1 || k2 ; key = k[:24] ; iv = k[-8:]
PBES2 moderne. Plus propre, et le cas courant aujourd'hui :
k = SHA1(globalSalt || primaryPassword)
key = PBKDF2-HMAC-SHA256(k, entrySalt, iterations, 32)
iv = 0x04 0x0e || <14-byte stored IV> # an NSS quirk: real IV is 16 bytes
Dans les deux cas, le mot de passe principal n'intervient que via ce premier
SHA1(globalSalt || password). Aucun mot de passe défini → le mot de passe
est la chaîne vide, et tout se déchiffre sans le moindre secret.
L'autovérification qui le rend fiable
Vous n'avez jamais à deviner si la dérivation a fonctionné. Déchiffrez
item2 et le texte en clair doit être exactement :
password-check\x02\x02
(la chaîne littérale password-check plus ses deux octets de remplissage
PKCS#7). En cas de correspondance, le mot de passe principal et toute la
dérivation sont corrects — un mauvais mot de passe échoue donc bruyamment au
lieu de produire silencieusement du charabia. C'est la même propriété
« le déchiffrement se prouve lui-même » que le
HMAC de la masterkey DPAPI.
Une fois la vérification faite, déchiffrez a11 pour récupérer la clé 3DES
de 24 octets, puis déchiffrez chaque entrée de logins.json avec elle
(3DES-CBC, retirez le remplissage PKCS#7) pour obtenir en clair le nom
d'utilisateur et le mot de passe.
Pourquoi cela tourne dans un onglet de navigateur
Toute la chaîne est SHA-1, HMAC-SHA1, PBKDF2, 3DES et AES — aucun appel
système, aucun magasin de clés. C'est donc un candidat idéal pour un module
WebAssembly : notre déchiffreur est une petite crate Rust (RustCrypto + un
minuscule lecteur ASN.1) compilée en WASM. La page lit key4.db et
logins.json, transmet les blobs bruts au WASM, et affiche les identifiants
déchiffrés — sans rien téléverser. Déposez les deux fichiers dans le
parseur dans le navigateur,
saisissez le mot de passe principal (ou laissez-le vide), et déchiffrez.
Pièges
- Mot de passe principal. Si l'utilisateur en a défini un et que vous ne
l'avez pas, la
password-checkéchoue et il n'y a aucun raccourci — il verrouille tout. - key3.db. Les très anciens profils utilisent le
key3.dbau format Berkeley-DB au lieu dekey4.db; le schéma est similaire mais le conteneur diffère. - N'oubliez pas la source du sel. Le sel global est dans
key4.db, pas danslogins.json— il vous faut les deux fichiers, à chaque fois.