veilletech.fr
20 oct. Feed du jour
#04 OAUTH Faille

Google OAuth : DPoP lie le refresh token à une clé

Une clé non exportable, c'est un redémarrage qui déconnecte tout le monde.

Google permet de lier un refresh token OAuth à une clé via DPoP (RFC 9449), annoncé le 15/10 ; seuls les refresh tokens sont liés, l'access token reste un Bearer. Un développeur a mesuré le comportement réel le 19/10 (Node 24.22.0, jose 6.2.12) : exemple officiel signé en DER, nonce accepté plusieurs fois, preuve rejouable près d'une heure, clé non extractable impossible à sauvegarder. Ce sont les mesures d'une seule personne ; son signalement à Google a été classé Won't Fix.

4 min de lectureavancévidéo 1:18
Partager
Sommaire5 sections
  1. Ce qui se passe
  2. Ce que le test a trouvé
  3. Les limites, dont celles du test
  4. Que faire
  5. À retenir

Ce qui se passe

Google accepte maintenant que le refresh token d'une application serveur soit lié à une clé cryptographique grâce à DPoP (Demonstrating Proof-of-Possession, norme RFC 9449, 2023). La nouveauté est le support de Google, pas le mécanisme : Okta et FusionAuth le proposent depuis un moment. L'annonce officielle date du 15 octobre 2026, mais l'auteur du test a retrouvé la trace du sujet dans les bonnes pratiques de Google dès le 20 mai, et la documentation du serveur web n'a été mise à jour que le 16 octobre.

Le principe : le serveur génère une paire de clés (EC P-256), signe un JWT, la preuve, à chaque appel du point d'accès de jetons, et Google émet un refresh token lié à la clé publique. Une base de données ou des journaux qui fuient ne livrent plus qu'un refresh token inutilisable sans la clé.

Ce que le test a trouvé

Le développeur a confronté la documentation officielle, le RFC 9449 et ses propres mesures à de vrais échanges de jetons, sur un client confidentiel (Node 24.22.0, jose 6.2.12, un compte et un client dédiés). Son verdict : l'implémentation fonctionne, la documentation non.

Choix de conception documentés mais différents du RFC. L'access_token reste un Bearer (seul le refresh token est lié) : Google invoque le coût de la vérification de signature à chaque requête d'une API à très fort trafic. Mesuré : 26,2 µs pour signer en ES256, 50,5 µs pour vérifier, soit environ 20 000 vérifications par seconde et par cœur. Lors de l'échange du code d'autorisation, le jti est un hachage déterministe, BASE64URL(SHA-256(code)), alors que le RFC exige une valeur aléatoire ; au refresh, il est aléatoire (192 bits).

Documentation contredite par les essais.

Non documenté. La preuve est acceptée de 3 600 s dans le passé mais seulement 10 s dans le futur : une horloge de serveur en avance de 11 secondes fait rejeter chaque preuve. La même preuve rejouée avec un refresh token valide passe à 59 minutes et échoue à 61. L'octroi est vérifié avant la preuve : un refresh token expiré donne invalid_grant quelle que soit l'erreur dans la preuve, ce qui masque les bugs. Google n'annonce pas DPoP dans .well-known/openid-configuration.

Les limites, dont celles du test

Le conseil de Google de créer la clé en extractable: false est incompatible avec Node : impossible de l'exporter, donc de la conserver ; au redémarrage, tous les refresh tokens liés à cette clé deviennent inutilisables, et seule une nouvelle autorisation de chaque utilisateur les remplace. Il faut une clé persistante, par exemple exportée puis chiffrée ou gardée dans un KMS : la sauvegarde de la clé devient la sauvegarde des refresh tokens. Migrer des utilisateurs existants exige aussi de révoquer l'ancien octroi ou de repasser par prompt=consent.

Le rejeu n'est pas une faille démontrée. L'auteur a signalé l'écart nonce/jti à Google, qui l'a classé Won't Fix (Infeasible), et il le reconnaît : un attaquant capable de lire la preuve lirait aussi le refresh_token et le client_secret du même corps de requête, et une preuve seule est rejetée en invalid_grant. Ce sont des mesures d'une personne sur des comptes de test ; la documentation de Google reste la référence.

Que faire

  1. Stockez-vous des refresh tokens Google côté serveur ? DPoP réduit l'effet d'une fuite de base de données. Prévoir d'abord la persistance chiffrée de la clé.
  2. Écrire la boucle de réessai sur use_dpop_nonce, et signer en format brut.
  3. Tester le refresh sans DPoP d'abord, puis l'ajouter, pour isoler les erreurs.
  4. Vérifier la synchronisation NTP des serveurs.

Source : DEV Community, 20 octobre 2026, mesures du 19 octobre. Source en anglais. Annonce de Google : Announcing DPoP Support for Google OAuth.