Pourquoi un code à usage unique valide échoue quand même à la connexion VPN
Un code refusé est généralement un problème d’ordre des demandes, d’enrôlement ou de temps, pas un mauvais code. Vérifiez ces points dans l’ordre avant de réenrôler quoi que ce soit.
Vous ouvrez le client VPN, saisissez le mot de passe, et les six chiffres de votre authentificateur reviennent comme invalides — deux fois, puis une troisième. Rien n’a changé sur le compte depuis hier, l’instinct est donc de supprimer l’entrée de l’authentificateur et de la rescanner — presque toujours la mauvaise réparation. Un code à usage unique n’est pas un second mot de passe : la passerelle doit le demander à une étape précise, depuis une entrée enrôlée précise, dans une fenêtre précise, et la plupart des refus surviennent avant que les chiffres ne soient comparés. Déterminez à quelle étape vous êtes avant de toucher à l’enrôlement.
L’ordre des demandes indique dans quel flux vous êtes
Trois formes couvrent presque toute connexion VPN qui implique un code. Dans la forme en deux étapes, le client envoie le mot de passe, le serveur émet un défi, et le client redemande. Dans la forme combinée, la passerelle veut mot de passe et code en une seule soumission — parfois concaténés, parfois après un séparateur que seul l’opérateur documente. Dans la forme navigateur, le client n’a aucun champ : le code va dans une page web, ou une validation push le remplace, et le client de bureau attend le résultat.
Se tromper de forme ressemble à un mauvais code. Collez un code frais dans un champ réservé au mot de passe et le serveur le lira comme une partie du mot de passe. Côté client, OpenVPN 2.4 et ultérieur rendent la différence visible : un serveur qui répond AUTH_FAILED,CRV1: attend une réponse au défi, le profil a donc besoin de static-challenge ou d’un fichier de mots de passe sous forme SCRV1:. Un profil plus ancien ne portant que auth-user-pass authentifie le mot de passe puis se bloque, ce qui ressemble dans le journal à un échec OTP alors qu’aucun code n’a été envoyé.
WireGuard mérite d’être cité comme l’exception : il authentifie des paires de clés et non des personnes, il n’y a donc aucun champ pour un code. Si un fournisseur en annonce un, quelque chose hors du tunnel l’accepte.
Ce que le serveur fait des six chiffres
Les codes basés sur le temps suivent RFC 6238, qui enveloppe la construction plus ancienne à compteur de RFC 4226 : un HMAC sur un facteur mobile, tronqué à six chiffres par défaut. Pour TOTP, le facteur mobile est l’heure Unix courante divisée par un pas, 30 secondes sauf si l’opérateur l’a modifié. Le vérificateur ne compare jamais vos chiffres à une valeur stockée ; il recalcule le code attendu à partir de la graine partagée et accepte normalement aussi les pas voisins, si bien qu’un aller-retour lent ne fait pas échouer la connexion.
Deux conséquences passent inaperçues. Le fuseau horaire du téléphone n’a aucune importance, car la graine ne voit jamais d’horloge locale — seul compte l’écart par rapport à l’UTC réel, et un appareil qui avance de deux minutes échoue à chaque code tout en affichant un compte à rebours propre. Et un code est à usage unique par conception : le vérificateur retient la dernière valeur de compteur qu’il a acceptée pour cette graine, donc les mêmes chiffres soumis à nouveau sont refusés même à l’intérieur de la même fenêtre. Un client qui réessaie après un délai peut brûler le code qu’il vient d’envoyer.
Les jetons à compteur déplacent leur compteur à chaque appui, ils se désynchronisent donc après que des codes ont été générés sans jamais être soumis ; la réparation habituelle est une fenêtre de resynchronisation qui accepte quelques valeurs d’avance. Si l’opérateur a enrôlé HOTP et que votre application affiche TOTP, chaque valeur est fausse d’une manière qui ressemble à un problème d’horloge. L’URI d’approvisionnement derrière le QR code nomme le type, les chiffres, la période et le hachage.
Des échecs qui ressemblent à un mauvais code
L’entrée enrôlée est le coupable le plus courant. Les applications d’authentification étiquettent les entrées par émetteur et par compte, si bien que quiconque détient deux identités sur le même VPN peut se retrouver avec deux entrées dont les libellés diffèrent d’un caractère. Un code n’est valide que pour la graine qui l’a généré et pour rien d’autre. Comparez le libellé du téléphone au nom d’utilisateur que le serveur enregistre, pas à ce dont vous vous souvenez avoir saisi.
Deuxièmement, un jeu de paramètres qui ne correspond pas. Une graine enrôlée avec huit chiffres, une période de 60 secondes ou SHA-256 produit des codes qu’un vérificateur attendant six chiffres, 30 secondes et SHA-1 refuse, et côté client cela est indiscernable d’un mauvais code. Cela apparaît après des migrations où l’entrée a été rajoutée à partir d’une photo du QR code, ou où l’enregistrement du jeton a été reconstruit avec les valeurs par défaut.
Troisièmement, cela ne concerne pas le code. Les passerelles renvoient généralement un unique échec d’authentification générique pour un mauvais mot de passe, un code expiré et un compte désactivé, délibérément, afin que personne ne puisse sonder quel facteur a échoué. Lisez le journal de la passerelle ou de RADIUS plutôt que le bandeau du client : un défi jamais émis, un rejet d’accès sans attribut OTP et un refus de politique se ressemblent à l’écran et mènent à des étapes suivantes différentes.
Une courte séquence qui le circonscrit
Commencez par la vérification la moins coûteuse et arrêtez-vous à la première étape qui ne tient pas.
- Attendez un code frais, soumettez-le une fois et notez si le défi apparaît avant ou après l’acceptation du mot de passe.
- Faites correspondre le libellé de l’authentificateur au nom d’utilisateur que le serveur stocke.
- Confirmez que le profil peut répondre à un défi, comme dans le cas
CRV1ci-dessus. - Comparez l’horloge de l’appareil à une source de temps connue ; un écart de plus de quelques dizaines de secondes casse TOTP à lui seul.
- Demandez à l’opérateur si le jeton est TOTP ou HOTP, et quels chiffres, période et hachage ont été enrôlés.
Deux règles empêchent que cela n’aggrave les choses. Ne réessayez pas de nombreuses fois : beaucoup de passerelles comptent les échecs par compte et soit le verrouillent, soit commencent à retarder les réponses, si bien qu’une rafale transforme une petite erreur de configuration en ticket de support. Et ne réenrôlez pas le jeton comme diagnostic, car cela invalide l’ancienne graine ; si la vraie panne était un défi que le client n’a jamais envoyé, vous avez désormais deux changements à expliquer au lieu d’un.
Gardez la graine et les codes de récupération hors du circuit
Les six chiffres et le secret qui les sous-tend ne sont pas interchangeables. Un QR code d’approvisionnement ou une graine Base32 génère chaque futur code du compte, il relève donc de la même catégorie que le mot de passe : pas dans une capture d’écran, pas dans un commentaire de ticket, pas dans une discussion avec quelqu’un qui vous a contacté en premier. Les codes de récupération sont un mécanisme distinct — à usage unique, ils sautent souvent complètement la demande de code, et en dépenser un pour tester le compte supprime une solution de repli dont vous pourriez avoir besoin plus tard.
Un code ne rend pas non plus une page de connexion digne de confiance : une demande d’OTP sur un domaine similaire collecte un code valide et le rejoue dans la fenêtre, et le certificat reste ce qui vous dit où vous tapez.
Si la séquence aboutit à un profil incapable de répondre à un défi, corrigez le profil et retestez avec un code frais. Si elle aboutit à des paramètres d’enrôlement incompatibles, faites lire l’enregistrement du jeton par l’opérateur avant que quiconque scanne quoi que ce soit. Si elle aboutit à un verrouillage, attendez qu’il passe plutôt que de changer de client — c’est l’état du compte qui bloque, pas l’application.