Clés d’accès vs mots de passe, c’est la comparaison entre un secret partagé et une authentification par clé publique. Un mot de passe est connu de l’utilisateur et, sous forme hachée, du serveur, et il fonctionne sur n’importe quelle page qui le demande, y compris une fausse. Une clé d’accès (passkey) est un identifiant WebAuthn : une paire de clés créée pour un site, déverrouillée sur l’appareil de l’utilisateur par une empreinte digitale, le visage ou un code PIN, et utilisée pour signer un challenge neuf, lié à l’identité du site.
Vue d’ensemble
| Mot de passe | Clé d’accès | |
|---|---|---|
| Ce que prouve l’utilisateur | La connaissance d’un secret partagé | La possession d’une clé privée, déverrouillée localement par biométrie ou code PIN |
| Ce que stocke le serveur | Sel et condensat issu d’une fonction lente (Argon2id, scrypt, PBKDF2) | ID d’identifiant et clé publique (91 octets pour P-256 dans la démonstration ci-dessous) |
| Site d’hameçonnage qui imite le vrai | Reçoit le mot de passe si l’utilisateur le saisit | N’obtient rien d’exploitable : l’identifiant est lié au RP ID et à l’origine du site |
| Réutilisation d’un site à l’autre | Courante, ce qui permet le bourrage d’identifiants (credential stuffing) | Impossible : une paire de clés par site et par compte |
| Fuite de la base de données | Attaques hors ligne par essais successifs sur les condensats | Seules des clés publiques fuient ; rien à rejouer |
| NIST SP 800-63B-4 (juillet 2025) | Facteur unique : au moins 15 caractères, AAL1 | Multifacteur cryptographique : AAL2 ; les clés matérielles liées à l’appareil peuvent atteindre AAL3, les clés d’accès synchronisées non |
| Appareil perdu | Sans effet | Les clés d’accès synchronisées se restaurent avec le compte du fournisseur ; les clés liées à l’appareil exigent un identifiant de secours |
| Autres appareils | Se saisit n’importe où | Synchronisées entre les appareils du fournisseur ; code QR et proximité Bluetooth (hybride) pour les autres |
| Normes | Aucune, hormis les formulaires HTML et l’authentification HTTP | W3C WebAuthn Level 3 (recommandation depuis le 25 août 2026), FIDO CTAP 2.2 |
Ce qui se passe lors de la connexion
Lors de l’enregistrement, l’authentificateur (un téléphone, un ordinateur portable, un gestionnaire de mots de passe ou une clé de sécurité) crée une paire de clés pour l’ID de la partie de confiance (RP ID) du site, par exemple example.com, et le site stocke la clé publique. Lors de la connexion, le site envoie un challenge aléatoire ; le navigateur ajoute l’origine de la page aux données client signées, et l’authentificateur signe ces données conjointement avec un condensat du RP ID, après la vérification de l’utilisateur. Le serveur vérifie le challenge, l’origine, le condensat du RP ID et la signature.
Le script ci-dessous (Node.js 26.10, Apple M4 Max) montre ce que chaque approche laisse sur le serveur et quels contrôles arrêtent un site imitateur.
import { argon2Sync, createHash, generateKeyPairSync, randomBytes, sign, verify } from "node:crypto";
const sha256 = (b) => createHash("sha256").update(b).digest();
// What a password database stores: an Argon2id hash (OWASP minimum: 19 MiB, t=2, p=1)
const params = { nonce: randomBytes(16), memory: 19456, passes: 2, parallelism: 1, tagLength: 32 };
const t0 = performance.now();
const tag = argon2Sync("argon2id", { message: "correct horse battery staple", ...params });
const ms = performance.now() - t0;
console.log(`password: stores salt + ${tag.length}-byte Argon2id hash; one guess costs ${ms.toFixed(0)} ms`);
// What a passkey database stores: the credential's public key
const { privateKey, publicKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
const spki = publicKey.export({ type: "spki", format: "der" });
console.log(`passkey: stores a ${spki.length}-byte public key; a leak lets nobody sign in`);
// Authenticator: signs authenticatorData || SHA-256(clientDataJSON)
function getAssertion(rpId, origin, challenge) {
const clientDataJSON = Buffer.from(JSON.stringify({ type: "webauthn.get", challenge, origin }));
const authData = Buffer.concat([sha256(rpId), Buffer.from([0x05]), Buffer.alloc(4)]);
const signature = sign("sha256", Buffer.concat([authData, sha256(clientDataJSON)]), privateKey);
return { clientDataJSON, authData, signature };
}
// Relying party: the checks from WebAuthn §7.2
function check(a, challenge) {
const c = JSON.parse(a.clientDataJSON);
return {
challenge: c.challenge === challenge,
origin: c.origin === "https://example.com",
rpIdHash: a.authData.subarray(0, 32).equals(sha256("example.com")),
signature: verify("sha256", Buffer.concat([a.authData, sha256(a.clientDataJSON)]), publicKey, a.signature),
};
}
const challenge = randomBytes(32).toString("base64url");
console.log("real site:", check(getAssertion("example.com", "https://example.com", challenge), challenge));
console.log("look-alike:", check(getAssertion("examp1e.com", "https://examp1e.com", challenge), challenge));
password: stores salt + 32-byte Argon2id hash; one guess costs 25 ms
passkey: stores a 91-byte public key; a leak lets nobody sign in
real site: { challenge: true, origin: true, rpIdHash: true, signature: true }
look-alike: { challenge: true, origin: false, rpIdHash: false, signature: true }
Le cas du site imitateur est délibérément favorable à l’attaquant : en pratique, le navigateur refuse purement et simplement d’utiliser les identifiants de example.com sur un autre site, et aucune signature n’est produite. Même une signature obtenue en relayant la session porterait une origine et un condensat de RP ID erronés ; c’est pourquoi le NIST classe WebAuthn comme résistant à l’hameçonnage grâce à la liaison au nom du vérificateur (verifier name binding), contrairement aux mots de passe, aux codes à usage unique et aux approbations par notification push. Avec Argon2id, chaque tentative de mot de passe coûte quelques dizaines de millisecondes, mais les mots de passe courants ou réutilisés sont tout de même retrouvés rapidement ; une clé publique ne donne à l’attaquant rien à deviner.
Où chacun échoue encore
Les mots de passe échouent par la réutilisation (bourrage d’identifiants à partir de listes divulguées), par les pages d’hameçonnage et les proxys d’hameçonnage en temps réel, qui capturent aussi les codes à usage unique, et par les choix faibles. NIST SP 800-63B-4 exige donc au moins 15 caractères pour les mots de passe utilisés comme facteur unique (8 dans le cadre d’une authentification multifacteur), interdit les règles de composition et les changements périodiques, impose de vérifier les nouveaux mots de passe par rapport aux valeurs divulguées et courantes, et recommande d’autoriser au moins 64 caractères. Un gestionnaire de mots de passe dont la saisie automatique tient compte du domaine règle la réutilisation et une partie de l’hameçonnage, mais tous les utilisateurs n’en ont pas.
Les clés d’accès déplacent le risque vers deux endroits. Le premier est le compte du fournisseur : les clés d’accès synchronisées ne sont pas mieux protégées que le compte Apple, Google, Microsoft ou de gestionnaire de mots de passe qui les détient, ce qui explique pourquoi le NIST n’autorise pas les clés synchronisées et exportables au niveau AAL3. Le second est la récupération de compte sur votre site : si une clé d’accès perdue peut être remplacée au moyen d’un lien envoyé par e-mail ou d’un code SMS, les attaquants emprunteront ce chemin à la place. Les utilisateurs ont aussi besoin d’un second identifiant ou d’un fournisseur qui synchronise leurs clés pour ne pas se retrouver bloqués hors de leur compte.
Quand choisir l’un ou l’autre
- Sites et applications grand public : les clés d’accès comme méthode principale, créées juste après la prochaine connexion par mot de passe ; mots de passe facultatifs.
- Connexion des employés : clés d’accès via votre fournisseur d’identité ; clés de sécurité liées à l’appareil pour les administrateurs et les autres rôles AAL3.
- Comptes grand public à forte valeur (banque, e-mail, consoles cloud) : clés d’accès plus une procédure de récupération robuste, idéalement une seconde clé d’accès enregistrée.
- Appareils partagés ou bornes : connexion hybride depuis le téléphone de l’utilisateur plutôt que stockage d’identifiants sur l’appareil partagé.
- Protocoles sans WebAuthn (IMAP, anciens VPN, SSH) : mots de passe d’application ou flux OAuth qui démarrent dans un navigateur où les clés d’accès fonctionnent.
Notes de migration
- Ajoutez les clés d’accès à côté des mots de passe : après une connexion par mot de passe réussie, proposez de créer une clé d’accès, ou utilisez la création conditionnelle de WebAuthn Level 3 pour en ajouter une automatiquement lorsque le gestionnaire de mots de passe du navigateur prend en charge cette fonction.
- Utilisez l’interface de saisie automatique : appelez
navigator.credentials.getavecmediation: "conditional"sur la page de connexion, pour que les clés d’accès apparaissent dans les suggestions du champ du nom d’utilisateur. - Stockez ce dont vous avez besoin : ID d’identifiant, clé publique, compteur de signatures, transports, indicateurs de sauvegarde et l’AAGUID qui désigne le fournisseur ; la recherche d’AAGUID le fait correspondre à un produit.
- Réglez la récupération avant de supprimer les mots de passe : exigez une clé d’accès existante, une seconde clé d’accès enregistrée ou une vérification d’identité, pas seulement un lien envoyé par e-mail.
- Gardez les fournisseurs synchronisés grâce aux méthodes de signalement de Level 3 lorsque les utilisateurs suppriment des identifiants ou changent de nom, et déclarez les origines associées (related origins) si la connexion s’étend sur plusieurs domaines.
- Testez dans le bac à sable des clés d’accès et inspectez de vraies réponses avec l’inspecteur WebAuthn avant le déploiement.
Références
- W3C Web Authentication Level 3 (§7.1 enregistrement, §7.2 cérémonie d’authentification)
- NIST SP 800-63B-4, authentification et gestion des authentificateurs (§3.1.1.2 mots de passe, §3.2.5 résistance à l’hameçonnage, annexe B authentificateurs synchronisables)
- FIDO Alliance : Client to Authenticator Protocol 2.2
- OWASP : aide-mémoire sur le stockage des mots de passe
- RFC 9106, Argon2