Bases de la sécurité
Votre clé API a fuité. L'ordre dans lequel agir
Une clé API a fuité et vous ne savez pas par quoi commencer. Toutes les clés de votre frontend n'en sont pas une, et l'ordre compte plus que la vitesse.

En bref
- Si une clé API a fuité, ce qu'il faut faire en premier dépend de la clé. Les clés publiables ont leur place dans votre frontend et ne demandent rien.
- Pour un vrai secret, déterminez si la clé peut dépenser de l'argent. C'est la seule partie de cette histoire sur laquelle tourne une horloge.
- Arrêter une clé et remplacer une clé sont deux commandes distinctes chez chaque fournisseur, et la plupart vous laissent une fenêtre où les deux clés fonctionnent.
- Ensuite, lisez le journal du fournisseur sur la période où la clé était dehors, et laissez votre historique Git tranquille tant que la clé n'est pas morte.
Le message arrive le plus souvent par un rapport de scan, par GitHub qui vous annonce qu'un secret a été repéré dans un de vos dépôts, ou par quelqu'un qui a appuyé sur F12 sur votre site et vous a envoyé une capture d'écran. Quelle que soit la route, vous savez maintenant qu'une clé API à vous a fuité et vous ne savez pas par quoi commencer.
Voici ce que guide après guide rate : ils donnent la même réponse pour toutes les clés, et cette réponse est toujours de la faire tourner. Certaines de ces clés sont censées être publiques et ne demandent rien du tout. Parmi les autres, certaines font grossir une facture pendant que vous lisez ceci, et d'autres ont causé tous leurs dégâts le jour du déploiement. Ce sont trois situations différentes, et le premier geste n'est pas le même dans chacune.
D'abord, est-ce que cette clé est vraiment un secret ?
Souvent non, et les chiffres penchent assez pour le dire franchement.
Nous avons scanné 31 056 applications en ligne et lu le JavaScript que chacune envoie au navigateur. Le contrôle qui cherche des identifiants a pu répondre sur toutes, et 1 332 sont revenues avec au moins une clé qui méritait un signalement. 1 142 de celles-là étaient des clés API Google, le seul type du lot qui se trouve en général exactement là où il doit être.
Une clé API Google dans une page web, c'est ce qui fait marcher Google Maps. La clé dit quel projet paie, et ce qui empêche un inconnu de dépenser sur votre dos est la restriction posée sur la clé plutôt que son secret. La documentation de Google est nette sur les deux moitiés : "Unrestricted API keys are insecure", et "You are financially responsible for charges caused by abuse of unrestricted API keys." La réparation pour une clé pareille consiste à l'ouvrir dans la Cloud Console et à poser deux restrictions, une qui nomme votre site et une qui nomme les API que vous appelez vraiment. La clé elle-même ne change jamais.
L'autre famille qui a sa place dans le navigateur, ce sont les clés publiables.
Une clé Stripe pk_live_, et une clé Supabase sb_publishable_ ou anon, sont
faites pour être lues par chacun de vos visiteurs.
Quelles clés sont sûres dans votre frontend
est la version en quatre caractères de ce test.
Si vous préférez ne pas parcourir votre bundle à la main, notre scan gratuit lit votre site en ligne, liste les clés qu'il voit de l'extérieur et dit de quelle nature est chacune : scanner votre application. Cela prend une vingtaine de secondes et ne demande aucun compte.
Ma clé API a fuité. Par quoi je commence ?
Déterminez si la clé a un compteur.
Une clé à compteur vous facture chaque requête. Celle de Google, celle d'OpenAI,
celle d'Anthropic, et une clé AWS avec les mauvaises permissions derrière, sont
toutes des compteurs : le trafic de quelqu'un d'autre atterrit sur votre
facture, à un rythme que cette personne choisit et que vous ne voyez pas. Une
clé sans compteur lit ou écrit vos données à la place. Une clé Supabase
service_role en est l'exemple le plus clair, et rien là-dedans ne coûte de
l'argent à l'heure.
Cette seule question fixe l'ordre.
Si la clé a un compteur, arrêtez-la maintenant. La fonctionnalité qui s'en servait est cassée jusqu'à ce que vous déployiez le remplacement, et c'est le bon échange, parce que la facture est la seule partie de cette histoire qui continue de grossir pendant que vous réfléchissez.
Si la clé lit des données et qu'elle a été publiée dans votre bundle, la lecture a déjà eu lieu. Chaque visiteur qui a chargé la page en a une copie, et chaque robot parti chercher exactement cette chaîne de caractères aussi. Rien n'empire pendant que vous cherchez le bon ordre, et ce qu'il faut réussir, c'est de ne pas vous enfermer dehors de votre propre application en chemin.
Ce que chaque type de clé peut faire
| Clé | Dépense de l'argent | Lit vos données | Peut-on la restreindre à la place ? | Le remplacement casse-t-il l'application ? |
|---|---|---|---|---|
Supabase sb_publishable_ ou anon | Non | Seulement les lignes que vos règles autorisent | Sa place est dans le navigateur | Sans objet |
Stripe pk_live_ | Non | Non | Sa place est dans le navigateur | Sans objet |
Google AIza… | Oui, sur votre facture Cloud | Non | Oui, et Google dit d'essayer ça d'abord | La restreindre non. La faire tourner, oui. |
| Clé OpenAI ou Anthropic | Oui, au rythme d'un inconnu | Les fichiers et assistants du projet | Il n'existe pas de variante publiable | Oui, jusqu'à ce qu'un serveur à vous la détienne |
Stripe sk_live_ ou rk_live_ | Oui | Oui, clients et dossiers de paiement | Une clé restreinte est la version étroite | Non, il y a une fenêtre de sept jours |
AWS AKIA… et sa moitié secrète | Oui, y compris du calcul à l'heure | Vos buckets S3 | Deactivate, et c'est réversible | Non, vous pouvez détenir deux clés à la fois |
Supabase sb_secret_ ou service_role | Non | Chaque ligne de chaque table | Non | Oui, sur un ancien projet |
Deux remarques sur ce tableau, parce que toutes les deux changent la suite.
Une clé d'accès AWS, ce sont deux chaînes de caractères, un identifiant qui
commence par AKIA et une moitié secrète, et AWS exige les deux ensemble pour
signer une requête. Une chaîne AKIA seule dans un bundle n'est donc pas
utilisable, et la raison de la traiter comme urgente quand même, c'est que les
deux moitiés sont presque toujours collées ensemble. Ouvrez le fichier et
cherchez la seconde avant de décider dans quel cas vous êtes.
Le détail par fournisseur vit chez chaque fournisseur : une clé OpenAI, une clé API Google, une clé secrète Stripe et une clé service_role Supabase, la seule qui ait une procédure à elle.
Arrêter la clé et remplacer la clé sont deux boutons différents
Chaque fournisseur du tableau vous donne les deux, et la panique attrape le second.
- Google. Restreindre une clé ne change pas la chaîne de caractères, votre application continue donc de marcher. Leur guide de sécurité met cela avant tout le reste : "First try to restrict your API keys", et la rotation est la troisième option en descendant, pour quand une restriction n'est pas possible.
- Stripe. Expire key arrête une clé toute seule, sans remplacement. Leur position sur le moment de s'en servir ne contient aucune réserve : "If a restricted or secret API key is exposed or compromised, rotate it immediately even if you aren't sure anyone saw it." Ils séparent aussi les deux mots que tout le monde confond. L'exposure est le fait que la clé soit devenue visible là où elle n'aurait pas dû l'être. La compromise est la preuve que quelqu'un s'en est servi.
- AWS. Deactivate est la commande, et sa partie utile est qu'elle se défait. AWS dit de ne pas supprimer l'ancienne clé du tout tant que vous vérifiez encore : "we recommend that you do not immediately delete the first access key. Instead, choose Actions and then choose Deactivate." Si quelque chose que vous aviez oublié en a encore besoin, vous la rallumez.
- Supabase, sur un ancien projet. Il y a un interrupteur, et il couvre les
deux clés historiques.
anonetservice_rolesont signées par le même secret : désactiver celle dont vous voulez vous débarrasser arrête aussi celle dont votre frontend se sert. Les quatre étapes qui évitent ça se lisent mieux avant de toucher à l'interrupteur qu'après.
Lire le journal sur la période où la clé était dehors
La fenêtre s'ouvre avec le déploiement qui a livré la clé et se referme au moment où vous l'avez arrêtée. C'est sur cette plage de dates que vous filtrez le journal du fournisseur.
Chaque fournisseur du tableau en tient un. Stripe affiche les journaux de requêtes d'une seule clé, depuis le menu de débordement à côté de cette clé sur la page des API Keys. AWS pose une date de dernière utilisation sur chaque clé d'accès dans la console IAM sans aucune configuration, et enregistre les appels eux-mêmes dans CloudTrail. Google trace l'usage par clé dans la Cloud Console, ce qui est aussi la lecture que son propre guide vous demande de faire avant de changer quoi que ce soit à une clé. Supabase conserve des journaux d'API et de base de données pour le projet, et comment réduire la fenêtre sur une clé Supabase dit quoi y chercher.
Ce que vous cherchez, c'est du trafic que vous ne pouvez pas expliquer : des requêtes vers des tables ou des endpoints que votre application ne touche jamais, du volume à des heures où personne ne s'en servait, des suppressions que personne n'a faites. Regardez ensuite les données elles-mêmes, parce qu'un message de support à propos d'un enregistrement qui a changé tout seul est la façon dont la plupart de ces histoires se découvrent, et il arrive des semaines plus tard.
Souvent vous ne pouvez pas en avoir le cœur net, et Stripe en dit autant de sa propre détection : "Stripe doesn't guarantee detection of all exposed or compromised keys." Donc "rien dans le journal" est un résultat, et il vaut la peine d'être noté avec la plage de dates à côté.
Faire tourner la clé sans mettre l'application à l'arrêt
Trois des quatre fournisseurs ci-dessus vous laissent une fenêtre où l'ancienne et la nouvelle clé fonctionnent toutes les deux, et c'est ce qui évite la panne.
Stripe. "When you rotate a key in the Dashboard, both the old and new keys work for up to 7 days." La boîte de dialogue a aussi une option Now, et leur documentation est explicite : si vous la choisissez, l'ancienne clé est supprimée. C'est le bouton de l'urgence à compteur plutôt que celui d'une migration tranquille. Leur conseil sur le moment de lâcher l'ancienne est une mesure plutôt qu'une date : vérifiez ses journaux de requêtes et ne la faites expirer qu'une fois son volume de requêtes à zéro depuis quelques heures ou quelques jours.
Google. La rotation crée la nouvelle clé en lui donnant toutes les restrictions de l'ancienne et, dans leurs termes, "both the old and new key are accepted" pendant que vous déplacez vos applications. Si vous supprimez l'ancienne trop tôt et que quelque chose casse, il y a un retour possible : une clé API Google supprimée peut être restaurée dans les 30 jours.
AWS. La séquence est dans leur documentation et elle commence par la nouvelle clé : créez la deuxième clé d'accès pendant que la première est encore active, déplacez chaque application dessus, vérifiez la date de dernière utilisation de l'ancienne, désactivez-la, et supprimez-la seulement après. Un plafond à prévoir : un utilisateur IAM ne peut détenir que deux clés d'accès au maximum, donc une troisième application encore sur une ancienne clé n'a nulle part où aller.
Supabase, sur un ancien projet. Pas de fenêtre. La rotation directe des clés
historiques anon et service_role n'est plus prise en charge, donc en
invalider une est une migration vers la nouvelle paire de clés, et l'ordre des
quatre étapes est ce qui garde l'application debout.
Elle est toujours dans votre historique Git
Elle y est, et la documentation GitHub sur le sujet commence par vous envoyer ailleurs.
Leur page sur la suppression de données sensibles vous renvoie à la clé : "if the sensitive data you need to remove is a secret (e.g. password/token/credential), as is often the case, then as a first step you need to revoke and/or rotate that secret", puis : "Once the secret is revoked or rotated, it can no longer be used for access, and that may be sufficient to solve your problem. Going through the extra steps to rewrite the history and remove the secret may not be warranted."
La raison qu'ils en donnent, c'est ce qu'un force push n'atteint pas. Après avoir réécrit votre historique, les anciens commits sont toujours là "In any clones or forks of your repository" et "Directly via their SHA-1 hashes in cached views on GitHub". Le support peut effacer les vues en cache et les références dans les pull requests si vous le demandez, et il pose sa propre limite : il "will only assist in the removal of sensitive data in cases where we determine that the risk can't be mitigated by rotating affected credentials." Un fork garde sa copie dans tous les cas, et GitHub ne peut pas vous donner les coordonnées de son propriétaire.
L'ordre qu'ils décrivent est donc l'ordre pratique : révoquer la clé, puis décider si réécrire l'historique vaut ses effets secondaires. Révoquer atteint des copies qu'une réécriture n'atteint pas, dont celle d'un clone dont vous ignorez l'existence et celle d'une capture d'écran que quelqu'un a gardée.
Empêcher la prochaine d'entrer dans le bundle
Une clé entre dans votre bundle par un déploiement, et les déploiements continuent. Chacun d'eux est une occasion de plus pour qu'une valeur que vous avez mise dans un panneau de secrets finisse dans un fichier que le navigateur télécharge. C'est pour ça qu'un contrôle fait le mois dernier décrit l'application du mois dernier.
Notre scan gratuit répond à la question qui vous a amené ici.
- les neuf contrôles sur votre URL en ligne, en une vingtaine de secondes
- chaque clé qu'il voit de l'extérieur, classée plutôt que seulement repérée
- une note et les constats, sans compte
Reeve Monitor relance ces contrôles sans que vous le demandiez.
- les neuf contrôles toutes les heures, sur jusqu'à trois applications
- un message quand un résultat change, pour que la clé partie dans le déploiement de cette nuit n'attende pas que vous alliez voir
- si l'application répond, toutes les 60 secondes
- un rapport mensuel de ce qu'il a vu
Reeve Care garde une copie de votre base de données Supabase, pour les clés qui peuvent écrire.
- une copie chiffrée chaque nuit, conservée là où votre projet ne peut pas aller
- chaque copie vérifiée avant de compter, en comptant les lignes de chaque table
- une restauration en un clic quand vous en avez besoin
- vos fichiers téléversés aussi, dès que vous connectez un identifiant Storage
- tout ce que fait Monitor
Une clé qui a fuité et qui ne peut que lire laisse vos données là où elles
étaient. Une clé service_role, ou une clé AWS avec des droits d'écriture, peut
vider une table, et aucune rotation après coup ne ramène les lignes.
Le jour où un agent IA a supprimé une base de données de production
raconte à quoi cela ressemble de l'intérieur.
Ce qu'il faut faire tout de suite
Que faire
- Identifiez la clé avant d'y toucher. Une clé Supabase
anonousb_publishable_et une clé Stripepk_live_sont à leur place, et sur les 1 332 applications où nous avons trouvé une clé méritant un signalement, 1 142 avaient une clé API Google, qui demande une restriction plutôt qu'une rotation. - Demandez-vous si elle a un compteur. Si les requêtes de quelqu'un d'autre atterrissent sur votre facture, arrêtez la clé maintenant et laissez la fonctionnalité casser.
- Servez-vous de la commande qui arrête autant que de celle qui remplace : Expire key chez Stripe, Deactivate chez AWS, une restriction par site et par API chez Google.
- Remplacez-la ensuite dans la fenêtre du fournisseur. Stripe vous donne sept jours avec les deux clés en service, Google accepte les deux pendant la migration, et AWS vous laisse détenir deux clés d'accès à la fois.
- Lisez le journal du fournisseur sur la période entre le déploiement et l'arrêt, et notez ce que vous avez trouvé, y compris "rien".
- Gardez l'historique Git pour la fin. Une fois la clé révoquée, le guide de GitHub dit que réécrire l'historique ne se justifie peut-être pas du tout.
Si vous préférez parcourir tout cela sous forme de liste, la checklist de sécurité en 10 minutes couvre ce sujet et les autres choses qu'il vaut mieux couper dans une application qui vient de sortir.
FAQ
Ma clé API a fuité. Par quoi je commence ?
Déterminez si la clé peut dépenser de l'argent. Une clé qui vous facture chaque requête vous coûte quelque chose en ce moment même : arrêtez-la tout de suite et acceptez que la fonctionnalité qui s'en sert casse quelques minutes. Une clé qui ne fait que lire des données a déjà été lue si elle était publique, alors prenez le temps de faire le remplacement dans un ordre qui ne vous enferme pas dehors de votre propre application.
Révoquer ou faire tourner la clé en premier ?
Révoquez en premier si la clé a un compteur. Arrêter l'ancienne clé et en émettre une nouvelle sont deux commandes séparées chez chaque fournisseur, et seule la première ferme le trou. L'exception, c'est une clé que vous pouvez restreindre à la place : Google vous dit d'essayer une restriction par site et par API avant même de faire tourner une clé API Google.
Comment savoir si quelqu'un a utilisé ma clé ?
Réduisez la fenêtre à la période entre le déploiement qui a livré la clé et le moment où vous l'avez arrêtée, puis lisez le journal du fournisseur sur cette plage. Stripe affiche les journaux de requêtes par clé, AWS affiche une date de dernière utilisation sur la clé d'accès et enregistre les appels dans CloudTrail, Google trace l'usage par clé, et Supabase conserve des journaux d'API et de base de données. Vous cherchez des requêtes vers des choses que votre application ne touche jamais et du trafic à des heures où personne ne s'en servait. Souvent vous ne pouvez pas en avoir le cœur net, et c'est un résultat normal.
Est-ce que faire tourner ma clé va casser mon application ?
En général non, si vous utilisez la fenêtre que le fournisseur vous donne. Stripe garde l'ancienne et la nouvelle clé en service ensemble pendant sept jours. Google émet la nouvelle clé avec les restrictions de l'ancienne et accepte les deux pendant votre migration. AWS vous laisse détenir deux clés d'accès à la fois, donc la nouvelle est déjà active avant que l'ancienne parte. L'exception est un ancien projet Supabase, où l'interrupteur qui désactive les clés historiques emporte avec lui la clé de votre frontend.
La clé est dans mon historique Git. Supprimer le fichier suffit-il ?
Non, et réécrire l'historique n'est probablement pas la réponse non plus. La documentation GitHub dit de révoquer ou de faire tourner le secret d'abord, et qu'une fois que c'est fait, se lancer dans la réécriture de l'historique ne se justifie pas forcément. Un force push n'atteint pas les copies dans les forks et les clones, ni les vues en cache accessibles par le hash du commit. Rendre la clé inutilisable couvre toutes les copies d'un coup.