Déployer WebAuthn FIDO2 passwordless sur une application web
Section intitulée « Déployer WebAuthn FIDO2 passwordless sur une application web »Web Authentication (WebAuthn) permet de remplacer le mot de passe par une preuve cryptographique produite par un authentificateur local, par exemple Windows Hello, Touch ID ou une clé USB. Fast IDentity Online 2 (FIDO2) apporte le cadre d’interopérabilité. Le gain central est net : supprimer le secret réutilisable côté utilisateur. Le prix l’est aussi : gérer sérieusement les appareils perdus, les origines web et la récupération de compte.
Un accès sans mot de passe ressemble à une porte qui reconnaît une preuve personnelle plutôt qu’un code recopié sur un post-it. Cela évite qu’un faux site récupère ce code, tout en laissant à chacun la responsabilité de ne pas perdre son moyen d’ouvrir la porte.
→ Lire l’explication complète de la marmotte
Prérequis WebAuthn et choix d’architecture
Section intitulée « Prérequis WebAuthn et choix d’architecture »Déployer WebAuthn exige HTTPS en production. Aucun contournement : le navigateur refuse l’interface WebAuthn hors contexte sécurisé, sauf localhost pendant le développement. Le frontal TLS (Transport Layer Security) doit transmettre correctement le nom d’hôte original à l’application, via Host ou X-Forwarded-Host uniquement si le proxy est de confiance. Voir sécuriser les canaux TLS pour le socle réseau.
Côté serveur, utiliser une bibliothèque qui valide les objets CBOR (Concise Binary Object Representation), les signatures COSE (CBOR Object Signing and Encryption) et les données d’authentificateur. Ne jamais parser ces structures à la main. Pour Python, webauthn (souvent appelée py_webauthn) couvre le protocole. Pour Java, retenir webauthn4j. Pour Node.js, @simplewebauthn/server est le choix raisonnable.
Installer Python :
# Installer la bibliothèque WebAuthn et un serveur ASGI.python -m pip install "webauthn>=2.0" "fastapi>=0.110" "uvicorn[standard]>=0.27"
# Conserver les dépendances résolues pour une construction reproductible.python -m pip freeze | grep -E 'webauthn|fastapi|uvicorn'Choisir dès le départ entre deux parcours :
- Authentification avec identifiant de compte : l’utilisateur saisit son adresse ou son identifiant, puis sélectionne un authentificateur enregistré. C’est le flux le plus facile à raccorder à une application existante.
- Authentification sans identifiant : l’authentificateur fournit un identifiant utilisateur opaque au site. Cela nécessite des clés détectables, appelées discoverable credentials ou passkeys. C’est le meilleur parcours passwordless grand public.
La recommandation est d’utiliser les clés détectables pour les comptes humains, avec un identifiant utilisateur aléatoire et immuable de 32 octets. Ne jamais utiliser l’adresse électronique comme identifiant binaire WebAuthn : elle est modifiable, prévisible et elle finit dans des journaux là où personne ne l’avait invitée.
Prévoir une table webauthn_credentials contenant au minimum :
credential_id: identifiant binaire de l’authentificateur, unique ;user_id: identifiant interne stable du compte ;public_key_cose: clé publique COSE, stockée en binaire ;sign_count: compteur de signatures ;transports: USB, NFC (Near Field Communication), Bluetooth ou interne, indicatif seulement ;backup_eligibleetbacked_up: état de synchronisation de la clé ;created_at,last_used_at,revoked_at.
Chiffrer cette table au repos est souhaitable, mais elle ne contient pas de clé privée. Une fuite expose des identifiants et des clés publiques, pas un moyen de signer. Cela ne dispense pas de protéger les sessions ni les données métier : WebAuthn authentifie un utilisateur, il ne transforme pas une application vulnérable en licorne réglementaire.
Enregistrement d’un authentificateur FIDO2
Section intitulée « Enregistrement d’un authentificateur FIDO2 »L’enregistrement comporte deux requêtes. D’abord, le serveur génère un défi imprévisible, l’associe à une transaction serveur courte et l’envoie au navigateur. Ensuite, le navigateur retourne une attestation et une nouvelle clé publique. Le défi doit être stocké côté serveur, lié au compte et consommé une seule fois.
Utiliser une session serveur, Redis ou une table transactionnelle avec expiration de cinq minutes maximum. Éviter le défi dans un cookie signé ou dans un jeton JSON Web Token (JWT) autonome : l’expiration et la consommation unique deviennent alors des promesses philosophiques, ce qui est une base fragile pour une authentification.
# Exemple FastAPI : création d'une cérémonie d'enregistrement.# session_store représente Redis ou une base de données avec durée de vie courte.
from uuid import UUIDfrom webauthn import generate_registration_optionsfrom webauthn.helpers.options_to_json import options_to_jsonfrom webauthn.helpers.structs import ( AuthenticatorSelectionCriteria, ResidentKeyRequirement, UserVerificationRequirement,)
RP_ID = "login.example.com"RP_ORIGIN = "https://login.example.com"
def registration_options(user_id: UUID, login: str, session_id: str) -> str: options = generate_registration_options( rp_id=RP_ID, rp_name="Application Exemple", user_id=user_id.bytes, # Identifiant interne, aléatoire et stable. user_name=login, user_display_name=login, authenticator_selection=AuthenticatorSelectionCriteria( resident_key=ResidentKeyRequirement.REQUIRED, user_verification=UserVerificationRequirement.REQUIRED, ), timeout=300_000, # Cinq minutes, valeur indicative pour le navigateur. )
session_store.setex( f"webauthn:register:{session_id}", 300, options.challenge, ) return options_to_json(options)Au retour, appeler verify_registration_response avec expected_challenge, expected_rp_id et expected_origin. Exiger require_user_verification=True. La vérification utilisateur impose une action locale protégée par code PIN, biométrie ou mécanisme équivalent. Sans cette exigence, une clé branchée sur une machine déverrouillée peut suffire. Ce n’est pas le passwordless attendu pour un accès sensible.
Stocker exactement credential_id, credential_public_key et sign_count retournés par la bibliothèque. Vérifier que credential_id n’est pas déjà rattaché à un autre compte. Rejeter aussi toute réponse après consommation du défi, même si la signature est correcte.
L’attestation peut identifier le modèle d’authentificateur. Pour une application métier classique, demander attestation="none" limite la collecte de données et les problèmes de confidentialité. Exiger une attestation vérifiée n’a de sens que si une politique impose des matériels approuvés, avec une chaîne de confiance maintenue. Sinon, c’est une usine à certificats qui découvre un jour férié que les métadonnées ont expiré.
Connexion passwordless et vérification de signature
Section intitulée « Connexion passwordless et vérification de signature »À la connexion, générer un nouveau défi. Pour un parcours sans identifiant, ne fournir aucune liste de clés autorisées : l’authentificateur sélectionne une clé détectable et retourne son identifiant utilisateur. Pour un parcours avec identifiant, fournir la liste des credential_id actifs du compte dans allow_credentials.
La réponse signée doit être vérifiée contre la clé publique stockée pour le credential_id reçu. La bibliothèque valide la signature, le défi, le nom de domaine et le drapeau de présence utilisateur. Une signature valide avec une mauvaise origine reste un échec. L’ordre n’est pas négociable.
Le compteur sign_count mérite une règle explicite. Si le compteur retourné est inférieur ou égal à celui stocké, marquer la clé comme suspecte et demander une réauthentification ou une récupération supervisée. Certains authentificateurs synchronisés maintiennent un compteur à zéro ou ne garantissent pas une progression globale. Dans ce cas, ne pas bloquer aveuglément : journaliser l’événement, évaluer l’état backed_up, puis appliquer une politique de risque. Le compteur détecte certains clonages, il ne prouve pas qu’aucun clonage n’existe.
Après succès, créer une session applicative avec rotation de l’identifiant de session. WebAuthn ne protège pas contre le vol de cookie, la fixation de session, les scripts intersites (Cross-Site Scripting, XSS) ou une autorisation métier absurde. L’authentification et l’autorisation restent deux métiers différents, malgré les slides qui les marient avec trois flèches bleues.
Validation du RP ID et surface d’attaque
Section intitulée « Validation du RP ID et surface d’attaque »Le Relying Party Identifier (RP ID) est le nom de domaine auquel la clé est liée. Pour https://login.example.com, définir soit login.example.com, soit example.com si plusieurs sous-domaines explicitement contrôlés doivent partager les clés. Ne jamais dériver cette valeur de l’en-tête HTTP reçu. Elle doit venir d’une configuration immuable par environnement.
L’origine attendue inclut schéma et hôte exacts : https://login.example.com. Le RP ID ne contient ni schéma, ni port, ni chemin. Confondre les deux ouvre une faille silencieuse : la signature est cryptographiquement valide, mais pour un autre contexte web que celui attendu.
Contrôler les points suivants :
- refuser HTTP hors
localhostde développement ; - fixer une liste d’origines pour chaque environnement, sans joker ;
- faire terminer TLS avant toute redirection applicative ;
- limiter les en-têtes transférés aux adresses IP des proxies de confiance ;
- consommer les défis une seule fois, avec expiration courte ;
- appliquer une limitation de débit aux endpoints d’enregistrement et d’authentification ;
- ne jamais enregistrer les défis, réponses complètes ou clés publiques dans des logs accessibles largement.
Le hameçonnage par domaine voisin (login.example.com.attacker.tld) échoue si le RP ID et l’origine sont fixes. En revanche, une compromission du vrai domaine, du JavaScript servi ou de la session existante reste hors du périmètre de la signature. Les fondations d’identité et la surface des menaces doivent couvrir ce reste, pas une invocation de FIDO2 au tableau blanc.
Récupération, révocation et exploitation des clés
Section intitulée « Récupération, révocation et exploitation des clés »Un authentificateur perdu est un incident d’accès, pas une exception exotique. Exiger l’enregistrement d’au moins deux authentificateurs avant de retirer le mot de passe. Accepter par exemple une passkey synchronisée et une clé matérielle indépendante. Deux clés synchronisées dans le même compte de plateforme ne constituent pas deux facteurs indépendants, seulement deux icônes très rassurantes.
Prévoir un facteur de secours distinct : procédure assistée avec vérification d’identité proportionnée au risque, code de récupération à usage unique conservé hors ligne, ou authentification fédérée déjà maîtrisée. Le lien de réinitialisation envoyé vers une boîte mail compromise ne restaure pas un accès, il déplace le mot de passe dans une autre boîte aux lettres.
La révocation doit définir deux actions séparées :
- marquer immédiatement l’authentificateur comme révoqué pour bloquer toute nouvelle connexion ;
- invalider les sessions actives du compte si la perte est déclarée comme compromission.
Sauvegarder la base des identifiants et clés publiques, ainsi que les procédures de récupération. Les clés privées restent dans les authentificateurs : aucune sauvegarde serveur ne les recrée. Superviser les taux d’échec de vérification, les erreurs d’origine, les réutilisations de défi, les baisses de compteur et les opérations de récupération. Une alerte sur un défi réutilisé est plus instructive qu’un graphique pastel sur le nombre de passkeys.
Tester au minimum Chrome, Firefox et Safari sur Windows Hello, Touch ID, Android et une clé USB. Tester aussi les navigateurs intégrés d’applications mobiles : plusieurs cassent les redirections ou le stockage de session, puis accusent la cryptographie avec le calme d’un grille-pain diplomate.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans preuve cryptographique liée au domaine, l’authentification repose sur un secret rejouable : un mot de passe capté une fois sert partout où il a été réutilisé, et un relais de hameçonnage placé entre l’utilisateur et le site transmet aussi bien le mot de passe que le code à usage unique, puisque rien ne vérifie l’origine web. La traçabilité se dégrade également : en l’absence d’identifiant de clé propre à chaque appareil, une connexion suspecte ne s’attribue qu’à une adresse IP et un agent utilisateur, et la révocation devient une réinitialisation globale du compte au lieu du retrait d’un authentificateur précis. Les signaux de détection associés disparaissent avec elle : réutilisation de défi, erreurs d’origine, régression de compteur de signatures ne sont plus observables, et la reprise après perte d’accès s’improvise au cas par cas.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »Le site commence par demander à l’appareil de créer une nouvelle preuve personnelle pour un compte. Il envoie une valeur aléatoire, valable peu de temps et utilisable une seule fois. L’appareil demande alors une validation locale, par exemple un code ou une empreinte, puis crée une partie privée qu’il garde et une partie publique qu’il transmet au site. Le site conserve uniquement la partie publique avec l’identifiant du compte.
Pour se connecter plus tard, le site produit une nouvelle valeur aléatoire. L’appareil valide la personne présente, puis signe cette valeur avec sa partie privée. Le navigateur envoie le résultat au site. Le site utilise la partie publique stockée pour vérifier que la signature correspond bien, que la valeur n’a pas expiré et que la demande vient du bon nom de domaine. Si une de ces vérifications échoue, aucune session ne doit être créée.
Le site doit aussi prévoir la perte d’un appareil. Une deuxième preuve d’accès ou une procédure de récupération contrôlée permet alors d’ajouter un nouvel appareil et de désactiver l’ancien. Cette méthode ne protège pas une session déjà volée, un site compromis ou des droits mal configurés. Elle évite surtout le vol et la réutilisation des mots de passe.
À lire aussi
- Déployer FIDO2 pour comptes à privilègesGuide pratique de priorisation et de déploiement progressif de l’authentification multifacteur résistante au phishing pour les comptes à privilèges.
- Configurer Windows Hello for Business sans mot de passeGuide pratique de déploiement de l’authentification sans mot de passe sur Active Directory avec Windows Hello for Business.
- Formats, bibliothèques et API : rendre la crypto utilisableDonner une structure web-first pour choisir des API sûres, concevoir des formats robustes et éviter les ambiguïtés de sérialisation.
- Configurer OIDC entre Keycloak et application interneGuide pratique pour fédérer une application interne avec un fournisseur d’identité OpenID Connect, du client OIDC à l’exploitation en production.
Pages voisines
La citent
- Déployer FIDO2 pour comptes à privilèges
- Mener une simulation de phishing interne éthique avec GoPhish
- Authentifier les personnes et les services
- Gérer les accès externes des prestataires
- Gouverner les facteurs d’authentification en entreprise
- Onboarding sécurité : règles à retenir dès l’arrivée
- Principes de sécurité, identité et cryptographie utile au réseau
Déployer WebAuthn FIDO2 passwordless sur une application web
Elle cite
: les deux pages se citent l’une l’autre.
