Résumé
- RFC 10017 place le BFF complet devant le backend médiateur de jetons et le client OAuth exécuté dans le navigateur : le serveur confidentiel conserve les jetons d’accès, les jetons de renouvellement et ses propres identifiants.
- Cette architecture empêche plusieurs vols et l’échange autonome d’un nouveau code, mais un script hostile déjà présent dans l’origine autorisée peut encore faire appeler le BFF par la session de l’utilisateur.
- La maîtrise réelle exige une origine par application, une chaîne logicielle surveillée, des cookies et protections CSRF vérifiables, des routes BFF fermées par hôte, chemin et méthode, puis une autorisation métier et une preuve de l’effet produit.
Le test d’intrusion est rassurant. Aucun jeton n’apparaît dans localStorage, dans IndexedDB, dans la mémoire de la page ni dans les journaux du navigateur. Le cookie de session est HttpOnly. Le BFF garde les secrets OAuth et relaie les appels vers l’API comptable.
Quelques jours plus tard, une dépendance JavaScript compromise appelle /bff/payments/change-account. Le navigateur joint automatiquement le cookie. Le BFF retrouve le jeton correspondant, l’ajoute à la requête sortante et transmet l’ordre. L’API voit un jeton valide, une audience correcte et un droit suffisant. Rien n’a été exfiltré ; pourtant, le compte destinataire a changé.
C’est le point le plus important de RFC 10017, publié en août 2026 comme Best Current Practice de l’IETF. Retirer les jetons du navigateur constitue une amélioration majeure. Cela ne transforme pas chaque instruction émise dans l’origine de l’application en expression de la volonté de l’utilisateur.
Le code hostile hérite d’une position, pas seulement de données
RFC 10017 part d’une hypothèse plus exigeante que le simple vol d’un secret. Quand du JavaScript ou du WebAssembly hostile s’exécute dans le contexte de l’application, il dispose des mêmes privilèges navigateur que le code légitime. Il peut lire ce qui est accessible à la page, communiquer avec les contextes de même origine, modifier le déroulement du programme et émettre des requêtes depuis l’origine acceptée.
Le document distingue quatre scénarios. Le premier copie une fois les jetons disponibles. Le deuxième installe un prélèvement persistant et récupère chaque nouvelle valeur. Le troisième ignore le stockage et déclenche un nouveau flux Authorization Code afin d’obtenir un jeu de jetons indépendant. Le quatrième ne vole rien : il utilise directement le navigateur et ses mécanismes d’authentification pour appeler le service.
Cette séparation évite de surévaluer les remèdes. Une durée courte réduit la fenêtre d’un jeton déjà volé, mais pas celle d’un script qui attend son remplaçant. La rotation du jeton de renouvellement peut révéler une réutilisation, mais l’attaquant persistant peut prendre la dernière valeur et empêcher l’application de la présenter. Un Web Worker isole un objet stocké ; il n’interdit pas nécessairement un nouveau flux. DPoP limite l’usage hors appareil d’un jeton lié à une clé, mais un attaquant dans un client navigateur public peut lancer un flux avec sa propre clé ou exploiter l’interface de signature présente.
PKCE reste obligatoire et utile. Il empêche qu’un code d’autorisation intercepté soit échangé par une autre instance. Il ne fournit pas à l’Authorization Server un moyen pratique de distinguer le bon programme d’un programme hostile déjà installé dans l’origine enregistrée.
Le BFF ferme trois portes et devient la quatrième
Dans le modèle BFF complet, le composant serveur est le client OAuth confidentiel. Il utilise le flux Authorization Code avec PKCE, conserve les jetons, associe ceux-ci à une session par cookie et relaie tous les appels aux Resource Servers. Le frontal ne reçoit jamais de jeton OAuth.
Trois gains sont nets. Il n’existe plus de jeton navigateur à copier une fois ou à récolter continuellement. Le script hostile ne peut pas non plus échanger seul un code comme client confidentiel, faute d’identifiants serveur. Enfin, HttpOnly empêche la lecture directe de l’identifiant de session et limite la conversion d’une prise de contrôle locale en session portable.
La quatrième porte reste ouverte par définition : l’application doit pouvoir demander au BFF d’agir. Un code hostile dans la même origine peut imiter cette demande. Le cookie protégé est joint, le BFF traduit la session en jeton, puis l’API reçoit un appel techniquement normal. L’attaque devient une utilisation abusive du canal de commande, non un vol du matériau OAuth.
Le BFF doit donc être une médiation sémantique, pas un proxy universel. RFC 10017 impose de contrôler strictement les destinations sortantes. Chaque route publique doit correspondre à des hôtes et chemins connus ; les méthodes doivent être restreintes. Une valeur fournie par le navigateur ne doit pas choisir librement l’URL à laquelle le BFF enverra le jeton. Sans cette discipline, la garde du secret au serveur alimente un relais doté de l’autorité complète de l’utilisateur.
Les cookies créent leur propre contrat. Le RFC exige Secure et HttpOnly, recommande SameSite=Strict, un chemin /, l’absence de l’attribut Domain et un préfixe lié à l’hôte. Ces règles protègent la session sans éliminer le CSRF : puisqu’un navigateur peut joindre le cookie à une requête, chaque route qui modifie l’état doit vérifier l’origine et un mécanisme anti-CSRF adapté.
La médiation de jetons protège la durée, pas toute la portée
Le backend médiateur est un compromis distinct. Il demeure client confidentiel et garde le jeton de renouvellement, mais remet le jeton d’accès au navigateur. Le frontal appelle alors directement les Resource Servers.
Ce modèle évite au serveur de relayer toutes les réponses. Il empêche aussi le script de voler le jeton de renouvellement ou de compléter seul un nouveau flux comme client confidentiel. En revanche, le jeton d’accès reste exposé. Même si une zone isolée le cache, le code hostile peut demander au backend médiateur d’en fournir un nouveau au titre de la session existante.
Le partage des responsabilités complique également DPoP : le backend acquiert le jeton tandis que le navigateur devrait produire la preuve d’usage. RFC 10017 ne normalise pas cette division. Une flèche entre deux boîtes ne constitue donc pas une preuve de contrainte d’émetteur. Le RFC recommande d’examiner un BFF complet avant de retenir ce modèle, et de documenter la contrainte qui rend le proxy complet impraticable.
Le navigateur seul ne devient pas confidentiel
Dans le troisième modèle, le code du navigateur est le client OAuth. Un secret incorporé au paquet ne peut pas rester secret et ne transforme pas ce client public en client confidentiel. Authorization Code avec PKCE, URI de redirection exactes, protection CSRF et règles strictes de renouvellement sont indispensables, mais ils ne suppriment pas les quatre scénarios une fois l’origine compromise.
L’origine devient ainsi une unité de gouvernement technique. Elle est le triplet schéma, hôte et port. RFC 10017 recommande de ne déployer qu’une application par origine, afin que CORS, CSP et l’isolation des contextes correspondent à une responsabilité claire. Plusieurs produits indépendants sous la même origine partagent davantage de pouvoir que leur organigramme ne le laisse croire.
CORS ne doit pas être gonflé en autorisation. Il règle l’exposition d’une réponse cross-origin au navigateur ; il ne décide ni la validité du jeton ni le droit sur l’objet, et ne distingue pas deux scripts exécutés dans la même origine permise. De même, postMessage exige la vérification exacte des origines émettrice et destinataire.
Le Service Worker ne peut pas régner au-dessus de l’origine
Un Service Worker semble offrir un BFF local : il isole les jetons et les ajoute aux requêtes. RFC 10017 ne recommande pourtant pas d’y placer le flux OAuth. Un code hostile peut désinscrire le worker, puis ouvrir un nouveau contexte qui ne bénéficie plus de son interception et déclencher un nouveau flux. L’isolement de mémoire est réel ; l’autorité permanente sur l’origine ne l’est pas.
Une clé Web Crypto non exportable possède une limite similaire. L’API empêche le code de lire la clé, sans garantir un TPM ni un stockage chiffré sur disque. Surtout, un code hostile peut parfois demander à l’environnement légitime d’utiliser cette clé. Ne pas pouvoir copier un instrument n’empêche pas toujours de l’actionner.
Suivre l’instruction jusqu’à son effet
La preuve opérationnelle doit relier la version de l’application et de ses scripts, l’origine, un hachage de session, le résultat CSRF, la route BFF, la destination et la méthode autorisées, l’audience et les scopes, la décision du Resource Server, l’état courant de l’objet, la clé d’idempotence et le résultat du commit. Les jetons, cookies et secrets bruts n’ont pas à être journalisés.
Les essais négatifs doivent séparer les capacités : lire un jeton, attendre sa rotation, obtenir un nouveau jeu, demander un jeton au médiateur, commander le BFF sans jeton, modifier la destination et répéter une opération après une réponse perdue. Un unique voyant « OAuth sécurisé » détruit précisément la distinction que RFC 10017 apporte.
La primauté du code en fonctionnement fournit le test : le mot BFF n’a pas d’autorité propre. Seuls les cookies, routes, coffres de jetons, règles de ressources et comportements d’échec réellement exécutés en ont. La spécification commune peut rester minimale et déterministe ; la décision irréversible doit demeurer chez l’opérateur responsable de l’effet.
Briefing des membres
Contexte approfondi du profil
Connectez-vous avec le bon niveau d'adhésion pour débloquer le briefing complet et les notes de source.
Réservé à Strategic Circle
Strategic Circle
Ouvert à tous les lecteurs. Débloquez les briefings de profil après adhésion et connexion.
Rejoindre Strategic CircleRéservé aux membres de Leadership Alliance
Leadership Alliance
Réservé aux propriétaires et dirigeants qualifiés d'actifs IP ; connectez-vous pour débloquer les briefings Alliance.
Rejoindre Leadership Alliance