Aller au contenu

Faille Facebook Voir en tant que 2018 et vol de jetons d'accès

Faille Facebook Voir en tant que 2018 et vol de jetons d’accès

Section intitulée « Faille Facebook Voir en tant que 2018 et vol de jetons d’accès »

Le 28 septembre 2018, Facebook annonce que des attaquants ont enchaîné trois bogues autour de la fonction « Voir en tant que » pour se faire délivrer les jetons d’accès d’autres utilisateurs. Près de 50 millions de comptes sont annoncés touchés, environ 90 millions sont reconnectés de force. Aucun mot de passe n’a fuité, et c’est tout l’intérêt de l’affaire : il n’y en avait pas besoin.

→ Lire l’explication complète de la marmotte

Chronologie de la faille et bilan des jetons volés

Section intitulée « Chronologie de la faille et bilan des jetons volés »

La faille entre dans le code en juillet 2017, avec une évolution de l’outil d’envoi de vidéo. Elle y dort plus d’un an. Facebook remarque un pic d’activité inhabituel à partir du 14 septembre 2018, identifie l’attaque le 25, corrige le 27 et communique le 28. Onze jours entre le premier signal et le diagnostic, pour un défaut invisible en revue de code puisque chacun des trois composants, pris isolément, faisait à peu près ce qu’on lui demandait.

La réponse tient en trois gestes : correction du bogue, invalidation des jetons des 50 millions de comptes identifiés plus 40 millions d’autres comptes ayant fait l’objet d’un « Voir en tant que » dans l’année (soit environ 90 millions de reconnexions forcées), désactivation de la fonction.

Le bilan du 12 octobre 2018 affine : 30 millions de jetons effectivement volés. Pour 15 millions de comptes, les attaquants ont consulté le nom et les coordonnées (téléphone, e-mail). Pour 14 millions, s’y ajoutent d’autres données de profil : genre, lieu, date de naissance, appareils, derniers lieux, recherches récentes. Pour 1 million, rien n’a été consulté. D’où le chiffre d’environ 29 millions de comptes dont des données ont été lues.

Les jetons volés étaient ceux de l’application Facebook. En principe, ils ouvraient aussi les services tiers reposant sur « Facebook Login » ; Facebook a indiqué n’avoir trouvé aucune trace d’un tel usage.

Les trois bogues enchaînés autour de Voir en tant que

Section intitulée « Les trois bogues enchaînés autour de Voir en tant que »

« Voir en tant que » (View As) est une fonction de confidentialité : elle montre à un utilisateur son propre profil tel qu’un autre le verrait. Lecture seule, par conception. Une fonction de confidentialité qui finit par distribuer des accès aux comptes : l’ironie est d’une sobriété exemplaire.

Les trois défauts :

  1. La vue en lecture seule affichait à tort un composeur de publication, celui qui propose de souhaiter un anniversaire. Ce composeur offrait l’envoi de vidéo.
  2. L’outil d’envoi de vidéo générait à tort un jeton d’accès doté des permissions de l’application mobile Facebook.
  3. Ce jeton était émis non pour l’utilisateur qui regardait, mais pour la personne dont il simulait le regard.

Le jeton se lisait dans le HTML (HyperText Markup Language) de la page. Il suffisait de le ramasser. Le schéma ci-dessous montre l’ordre des défauts et, surtout, la boucle finale : chaque compte pris fournit une liste d’amis, donc de nouvelles cibles.

Chaîne en huit étapes. L'attaquant affiche son profil vu par la cible dans Voir en tant que. Cette vue en lecture seule rend à tort le Composeur d'anniversaire (bogue 1), qui charge l'Outil d'envoi de vidéo. L'outil demande au Service de jetons un jeton aux permissions de l'application mobile (bogue 2), émis au nom de la cible et non du demandeur (bogue 3) dans la Page HTML, où l'attaquant le lit dans le code source. Il accède alors au compte de la cible, lit la liste d'amis et en tire de nouvelles cibles : la boucle est automatisée.

Aucun de ces trois bogues n’est spectaculaire. Le troisième est le seul qui compte vraiment : une confusion entre l’acteur (qui est authentifié) et le sujet (dont on affiche les données). Les deux premiers n’ont fait qu’amener un chemin de code d’émission de jeton là où il n’avait rien à faire. Le pivot par listes d’amis, automatisé, a transformé une confusion d’identité en propagation virale : le graphe social a servi de carnet d’adresses à l’attaque.

Jeton au porteur : une session ouverte jusqu’à révocation

Section intitulée « Jeton au porteur : une session ouverte jusqu’à révocation »

Un jeton d’accès au porteur, au sens d’OAuth 2.0 (Open Authorization) et de la RFC (Request for Comments) 6750, donne ses droits à quiconque le présente. Aucune preuve d’identité supplémentaire, aucun contrôle du présentateur. C’est sa raison d’être : éviter de redemander le mot de passe à chaque requête.

Trois conséquences, vérifiables sur n’importe quelle plateforme :

  • Changer le mot de passe ne sert à rien si le changement ne révoque pas explicitement les sessions et jetons existants. Facebook l’a d’ailleurs dit sans détour : les mots de passe n’étaient pas compromis et n’avaient pas à être changés.
  • L’authentification multifacteur ne protège pas davantage : le jeton est émis après elle. Un second facteur blindé garde l’entrée, pas le vestiaire.
  • La durée de vie du jeton est la durée de vie du vol, sauf révocation.

Le compromis central est là. Un jeton long, validé localement, sans appel à un référentiel, offre le confort (pas de reconnexion) et l’échelle (pas d’état partagé à interroger). On le paie le jour du vol : la fenêtre d’exploitation est égale à la durée de vie restante, et la révocation devient un chantier au lieu d’être une commande.

Position assumée : jeton d’accès à TTL (Time To Live) court, jeton de rafraîchissement à rotation et à usage unique, et un compteur de génération par compte que les services vérifient au moins sur les opérations sensibles. Le choix entre jeton autoporté, type JWT (JSON Web Token, JSON pour JavaScript Object Notation), et jeton opaque avec introspection (RFC 7662) dépend d’une seule question : quelle fenêtre d’exposition est tolérable après un vol ? Si la réponse est « aucune », le jeton autoporté seul est disqualifié, quelle que soit l’élégance de sa signature. Le cycle de vie complet (durées, rotation, révocation de masse) est traité dans Gérer la durée de vie des jetons OAuth.

Émission du jeton : vérifier le sujet, l’acteur et le client

Section intitulée « Émission du jeton : vérifier le sujet, l’acteur et le client »

Le bogue 3 est un cas d’école d’adjoint confus : un composant privilégié agit pour le compte d’une identité qu’il lit dans le mauvais contexte. Le service d’émission a pris le « profil affiché » pour « l’utilisateur authentifié ». Dans beaucoup de bases de code, les deux vivent dans des variables aux noms voisins ; un jour, quelqu’un se trompe de variable, et les tests passent puisque, hors usurpation, les deux valeurs sont identiques.

Règles d’émission qui auraient cassé la chaîne, chacune indépendamment :

  • Le sujet du jeton vient de la session authentifiée, jamais d’un paramètre de requête ni du contexte de rendu. Si acteur et sujet diffèrent, l’émission est refusée par défaut.
  • Le client du jeton est vérifié contre la surface qui le demande. Un jeton aux permissions de l’application mobile, demandé depuis une page web par un module de vidéo, est une anomalie détectable à l’émission. C’est le cousin direct de la confusion de scope OAuth.
  • Les points d’émission sont inventoriés et centralisés. Un module fonctionnel qui fabrique des jetons de son propre chef est un fournisseur d’identité clandestin.
  • Un jeton n’a rien à faire dans le HTML d’une page : tout ce qui y figure est lisible par l’utilisateur de la page, et par n’importe quel script injecté via un cross-site scripting.

Un piège classique mérite d’être nommé. La liaison du jeton à une clé détenue par le client, façon DPoP (Demonstrating Proof of Possession, RFC 9449), empêche le rejeu d’un jeton volé en transit ou sur disque. Elle n’aurait rien empêché ici : le jeton était émis directement au demandeur, donc lié à la clé du demandeur, donc à celle de l’attaquant. La preuve de possession protège contre le vol, pas contre une émission erronée.

Fonctions d’usurpation : une surface d’attaque à part entière

Section intitulée « Fonctions d’usurpation : une surface d’attaque à part entière »

« Voir en tant que », impersonation de support, « se connecter comme ce client » dans une console d’administration : même famille. Toutes introduisent deux identités dans une même requête, et tout le code écrit en supposant qu’il n’y en a qu’une devient suspect. Ces fonctions naissent comme un outil de confort, vivent sans propriétaire et meurent dans un rapport d’incident.

Deux façons de les construire, et elles ne se valent pas :

  • Le rendu filtré : la session reste celle de l’acteur, et la vue applique les règles de visibilité du sujet simulé. Aucun changement d’identité, aucune raison d’émettre quoi que ce soit. C’est le bon modèle pour une fonction en lecture seule.
  • Le changement de contexte : la requête s’exécute « comme » le sujet. Tout composant qui lit l’utilisateur courant obtient alors la mauvaise réponse, y compris ceux ajoutés plus tard par une équipe qui ignore l’existence du mode.

Quand le second modèle est inévitable (support, délégation), les garde-fous sont non négociables : liste blanche des composants autorisés dans ce mode (le composeur n’y figurait manifestement pas, il s’y est invité), interdiction des chemins de code qui émettent des jetons ou modifient des secrets, journalisation systématique des deux identités, et test de non-régression qui échoue dès qu’un nouveau composant s’affiche en mode usurpation sans y avoir été admis. La faille est restée plus d’un an dans le code avant l’attaque : un test de ce genre l’aurait signalée le jour de la fusion.

Révocation massive des jetons : prérequis et exploitation

Section intitulée « Révocation massive des jetons : prérequis et exploitation »

Invalider environ 90 millions de comptes suppose une capacité qui existait avant l’incident. Elle ne s’improvise pas pendant.

Prérequis de plateforme :

  • Un état de révocation consultable par les services : registre de sessions par compte, ou compteur de génération par compte comparé à celui inscrit dans le jeton. Le compteur passe mieux à l’échelle qu’une liste de jetons révoqués : une écriture par compte, quel que soit le nombre de jetons en circulation.
  • Un point de révocation unitaire (RFC 7009) et une commande de masse acceptant un critère (« tous les comptes ayant fait l’objet de telle fonction sur telle période »). Sans journal d’usage de la fonction, ce critère n’existe pas et il ne reste que l’option nucléaire : rotation de la clé de signature, déconnexion générale.
  • Un frontal d’authentification dimensionné pour la vague de reconnexions qui suit, canaux de récupération de compte inclus.

Conditions d’exploitation :

  • Supervision de l’émission : débit de jetons par point d’émission, jetons émis pour un client incohérent avec la surface, jetons utilisés depuis une adresse IP (Internet Protocol) ou un client sans rapport avec leur émission, émission où acteur et sujet diffèrent. Le pic du 14 septembre a été vu ; il a fallu onze jours pour le lire.
  • Exercice régulier de la révocation de masse, sur un périmètre réel. Une procédure jamais jouée est une hypothèse.
  • Sauvegarde et reprise : restaurer l’état de révocation depuis une sauvegarde antérieure ressuscite les jetons révoqués entre-temps. La reprise doit échouer fermée (toute session antérieure à la restauration est invalide).
  • Rotation des clés de signature planifiée et testée, puisqu’elle sert aussi de dernier recours.

Exemple : décision d’émission de jeton en mode usurpation

Section intitulée « Exemple : décision d’émission de jeton en mode usurpation »

La logique ci-dessous décrit ce qu’un service d’émission centralisé vérifie avant de signer quoi que ce soit. Trois branches : émission normale, refus, délégation explicite.

Séquence entre trois acteurs : Module applicatif, Service de jetons et Journal. 1. Le module envoie au service une demande (session, sujet visé, client, permissions). 2. Le service fixe l'acteur comme l'identité de la session authentifiée. Puis quatre réponses possibles vers le module : jeton normal si l'acteur est le sujet visé et le client cohérent avec la surface ; refus et alerte si le client est incohérent avec la surface ; refus et alerte si l'acteur diffère du sujet visé sans délégation enregistrée ; jeton délégué restreint si une délégation est enregistrée. Enfin, le service écrit au journal acteur, sujet, client, permissions et décision.

Appliquée à l’affaire, la troisième branche suffit : acteur et sujet diffèrent, aucune délégation n’existe pour une fonction en lecture seule, l’émission est refusée et une alerte part. La deuxième branche aurait suffi aussi.

Pour la dernière branche (support, délégation légitime), la RFC 8693 (échange de jetons) fournit la représentation : la revendication act nomme l’acteur réel, distinct du sujet. Contenu type d’un tel jeton :

{
"sub": "utilisateur-cible",
"act": { "sub": "agent-support-42" },
"scope": "profil:lecture",
"client_id": "console-support",
"exp": 1700000300,
"iat": 1700000000
}

À lire ainsi : le sujet (sub) est le compte consulté, l’acteur (act.sub) est celui qui agit réellement, les permissions sont réduites à la lecture, la durée de vie est de 300 secondes (exp moins iat) et aucun jeton de rafraîchissement n’accompagne l’ensemble. Chaque service qui reçoit ce jeton peut refuser une opération d’écriture et journaliser les deux identités.

Ce que jetons courts et révocation ne garantissent pas

Section intitulée « Ce que jetons courts et révocation ne garantissent pas »
  • La révocation ne rattrape rien. Les données de 29 millions de comptes avaient été lues avant l’invalidation ; révoquer ferme la porte, sans faire revenir ce qui est sorti.
  • Un TTL court réduit la fenêtre, pas le vol. Une boucle automatisée qui pivote de compte en compte travaille en secondes.
  • La révocation chez le fournisseur d’identité ne ferme pas les sessions déjà ouvertes chez les services tiers. Un site qui a ouvert sa propre session à partir d’une connexion fédérée garde son cookie jusqu’à sa propre expiration, sauf mécanisme de déconnexion propagée ou revalidation périodique. Même logique que pour la fixation de session : l’état de session vit là où il a été créé.
  • La preuve de possession ne corrige pas une émission au mauvais sujet (voir plus haut).
  • Aucun de ces mécanismes ne remplace la détection : sans supervision de l’émission, la révocation n’est jamais déclenchée.

Le reste doit venir d’ailleurs : minimisation des données accessibles par jeton, permissions étroites par client, et revue de conception des fonctions qui manipulent deux identités.

Le 17 décembre 2024, la DPC (Data Protection Commission) irlandaise a infligé à Meta 251 millions d’euros d’amende au total pour cette violation, au titre du RGPD (Règlement général sur la protection des données). Deux décisions, deux articles : 11 millions d’euros au titre de l’article 33 (notification de la violation à l’autorité, jugée incomplète, et documentation insuffisante) et 240 millions au titre de l’article 25 (protection des données dès la conception et par défaut).

Lecture d’architecte : l’article 25 fait d’un défaut de conception un manquement en soi, indépendamment du soin apporté à la réponse. L’article 33 suppose de pouvoir décrire rapidement ce qui s’est passé, ce qui renvoie à la journalisation de l’émission des jetons : on ne documente pas ce qu’on n’a pas tracé. Pour la portée exacte de ces obligations dans une situation donnée, le texte du règlement et la décision de la DPC font foi, et l’analyse d’un cas particulier relève d’un conseil juridique.

Fallait-il changer son mot de passe après cette faille ?

Section intitulée « Fallait-il changer son mot de passe après cette faille ? »

Non, et Facebook l’a précisé : les mots de passe n’ont pas été compromis. La mesure efficace était l’invalidation des jetons, faite côté serveur. Sur une plateforme où le changement de mot de passe ne révoque pas les sessions existantes, le changer après un vol de jeton ne produit qu’un sentiment de devoir accompli.

Un jeton à durée de vie courte aurait-il empêché l’attaque ?

Section intitulée « Un jeton à durée de vie courte aurait-il empêché l’attaque ? »

Non. Il aurait réduit la durée d’usage de chaque jeton volé, pas leur obtention : tant que le défaut d’émission existait, l’attaquant pouvait redemander un jeton frais. La correction est à l’émission (vérifier sujet, acteur et client) ; le TTL court ne fait que limiter les dégâts une fois le jeton sorti.

Les services tiers utilisant la connexion Facebook étaient-ils exposés ?

Section intitulée « Les services tiers utilisant la connexion Facebook étaient-ils exposés ? »

En principe oui : les jetons de l’application Facebook ouvraient aussi les services reposant sur « Facebook Login ». Facebook a indiqué n’avoir trouvé aucune trace d’un tel usage. Côté service tiers, la leçon est générale : une session ouverte par fédération doit pouvoir être fermée quand le fournisseur d’identité signale une révocation.

Sans contrôle de l’acteur et du client à l’émission, tout module capable de demander un jeton devient un guichet d’identités : le premier composant chargé dans un contexte imprévu suffit. Sans journal des émissions et de l’usage des fonctions d’usurpation, le périmètre d’un incident est inconnaissable : impossible de cibler les comptes à invalider, il ne reste que la déconnexion générale ou l’attente. Sans révocation de masse exercée, le délai entre la correction du défaut et la fin effective des accès se mesure à la durée de vie des jetons en circulation. Et sans état de révocation partagé, chaque service continue d’honorer des jetons que le fournisseur d’identité considère comme morts.

Quand on se connecte à un site, il remet à l’application un petit code secret appelé jeton. Tant que ce code est valable, le site ne redemande plus le mot de passe : présenter le code suffit, peu importe qui le présente. Facebook proposait une fonction pour regarder son propre profil avec les yeux d’une autre personne. Trois erreurs de programmation se sont combinées. La première affichait, dans cette vue, un outil pour publier un message d’anniversaire. La deuxième faisait fabriquer un jeton par l’outil d’envoi de vidéo qui s’y trouvait. La troisième, la plus grave, fabriquait ce jeton au nom de la personne imitée, pas au nom de celle qui regardait. Le code apparaissait dans le texte de la page. Des attaquants l’ont copié, sont entrés dans le compte visé, ont lu la liste d’amis et ont recommencé sur chaque ami, avec un programme automatique. Changer de mot de passe n’aurait rien arrêté, puisque le jeton fonctionne sans lui. La seule parade consiste à annuler les jetons : Facebook l’a fait pour environ 90 millions de comptes, obligés de se reconnecter. Les données déjà lues, elles, étaient parties. La leçon : avant de fabriquer un jeton, vérifier pour qui on le fabrique, et savoir tous les annuler très vite.

À lire aussi

Pages voisines