Bases de la sécurité
Une clé AWS exposée dans le frontend n'est pas une clé OpenAI
Une clé d'accès AWS exposée dans votre frontend lit et écrit votre stockage. La solution est en général une presigned URL, pas une nouvelle clé.

En bref
- Une clé d'accès AWS exposée dans votre frontend est un autre problème qu'une clé qui ne peut que dépenser. Ce qu'elle atteint dépend de ses permissions.
- Deux chaînes signent une requête AWS : un identifiant qui commence par `AKIA`, et une moitié secrète. L'une sans l'autre ne signe rien.
- Une nouvelle clé collée au même endroit est la même trouvaille avec une autre valeur. La solution est une presigned URL, pour que le navigateur ne détienne aucun identifiant.
- Nous en avons trouvé une dans 9 des 31 056 applications scannées où ce contrôle est allé au bout.
Quelqu'un ouvre votre application dans un navigateur, appuie sur F12, et trouve
une chaîne commençant par AKIA posée dans votre code frontend. Ou GitHub vous
écrit qu'un commit poussé en mars dernier contient un identifiant AWS. Le
conseil qui accompagne les deux est toujours le même : faites tourner la clé.
Pour la plupart des clés, c'est presque toute la réponse.
Voici la partie que guide après guide se trompe. Une clé d'accès AWS exposée dans votre frontend n'est pas une clé qui dépense de l'argent. C'est une clé qui ouvre des choses, et les deux tournent mal dans des directions différentes. Une clé qui ne fait que dépenser est une carte de société : quelqu'un fait une note, vous bloquez la carte, et le dommage est un chiffre que vous lisez sur un relevé. Une clé qui ouvre des choses est le trousseau de l'entrepôt. Ce qu'elle atteint dépend de ce qui a été écrit dessus au moment où elle a été taillée, et cela se lit sur un seul écran de votre console AWS.
Que veut dire AKIA au début d'une clé ?
Cela marque une clé d'accès à longue durée, appartenant soit à un utilisateur
de votre compte, soit à l'identifiant root du compte. AWS note le préfixe dans
sa propre référence des identifiants : AKIA pour les clés d'accès, et ASIA
pour les temporaires que son service de jetons distribue. Les quatre premiers
caractères vous disent donc à quelle sorte vous avez affaire, et AKIA est
celle sans date d'expiration. Elle marche jusqu'à ce que quelqu'un l'éteigne.
Un identifiant AWS, ce sont deux chaînes, et une requête a besoin des deux pour
être signée. La première est l'identifiant de la clé d'accès, la partie qui
commence par AKIA. La seconde est la clé d'accès secrète, une chaîne plus
longue sans aucun préfixe devant. L'identifiant seul ne signe rien.
Cela vaut la peine d'être vérifié avant de décider à quel point la matinée est
mauvaise, car cela change la réponse. Si votre bundle porte la chaîne AKIA et
rien qui ressemble à sa moitié secrète, un visiteur lisant votre page a eu le
nom de la clé et pas la clé. En pratique, les deux voyagent ensemble, car ce
qui a mis l'une dans le navigateur a mis les deux : une paire de variables
d'environnement nommées AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY, lues
par du code censé tourner sur un serveur.
Que peut faire quelqu'un avec ma clé d'accès AWS ?
Tout ce que ses permissions autorisent, et rien au-delà. Il y a donc trois réponses.
Que peut-elle lire ? Chaque bucket S3 que la politique liste, et chaque objet dedans, y compris ceux vers lesquels vous n'avez jamais mis le moindre lien. Un bucket que rien dans votre application ne mentionne n'est pas caché à des identifiants qui ont le droit de le lister.
Que peut-elle écrire ? Les mêmes endroits, et c'est la moitié qu'on oublie. Une clé qui peut écrire peut aussi écraser et supprimer, et un fichier remplacé sur place ressemble exactement à celui que vous avez envoyé jusqu'à ce que quelqu'un l'ouvre.
Que peut-elle démarrer ? Celle-là a un compteur. Une politique assez large pour lancer du calcul lance du calcul, et cela facture à l'heure tant que ça tourne. Rien dans la copie que détient le navigateur ne limite la part de votre compte qu'elle atteint ; la politique est la seule limite.
La pire version de tout cela est une clé taillée pour l'identifiant root du
compte. AWS écrit dans ses propres consignes sur l'utilisateur root que
celui-ci a un accès sans restriction à tous les services AWS, aux ressources et
aux données de facturation, et déconseille de lui créer la moindre clé d'accès.
Si la clé de votre page commence par AKIA et appartient à l'utilisateur root,
ce qui est exposé est le compte entier et non un bucket.
Ne devinez donc rien de tout cela. Ouvrez IAM, trouvez l'utilisateur auquel la clé appartient, et lisez les politiques attachées. C'est un écran, et il répond aux trois questions ci-dessus.
Suffit-il de la faire tourner ?
Désactivez la clé exposée aujourd'hui, et ne mettez pas de remplaçante au même endroit.
Désactiver est le bon premier geste et c'est plus doux qu'il n'y paraît. AWS laisse un utilisateur détenir jusqu'à deux clés d'accès actives à la fois, et cela existe précisément pour qu'une remplaçante monte avant que l'ancienne ne descende. Désactiver laisse la clé en place et éteinte, et elle se rallume ; supprimer est irréversible, et la moitié secrète d'une clé supprimée ne se récupère pas. Donc désactivez, regardez ce qui casse, et supprimez après.
Ce qui ne termine pas l'affaire, c'est de créer une nouvelle clé et de la
coller là où était l'ancienne. La fuite n'a jamais vraiment porté sur cette
valeur-là. Elle portait sur un navigateur détenant un identifiant sans
expiration, et tout ce que votre page télécharge, chaque visiteur le
télécharge. Une chaîne AKIA toute fraîche dans le même bundle revient comme
la même trouvaille au scan suivant.
AWS le dit clairement dans son guide d'audit de sécurité, à propos des applications mobiles : elles ne devraient jamais contenir de clés d'accès intégrées, même chiffrées. Un bundle de navigateur se lit plus facilement qu'un binaire d'application, donc la même réponse vaut ici et coûte moins de travail à un inconnu.
Si vous avez déjà trouvé la clé dans un endroit public, l'ordre dans lequel vous travaillez compte, et la bonne suite quand une clé API fuite pose cet ordre pour les clés de toutes sortes.
Ce qu'une presigned URL fait à la place
Elle laisse le navigateur faire une seule chose à un seul fichier sans détenir le moindre identifiant.
Votre propre serveur garde la clé d'accès, là où un visiteur ne peut pas la lire. Quand votre page doit déposer un fichier dans S3 ou en sortir un, elle demande à votre serveur, et votre serveur signe une URL pour cet objet-là et cette action-là et la rend. Le navigateur se sert de l'URL et n'obtient rien d'autre avec. AWS décrit une presigned URL comme un accès temporaire aux objets S3 sans identifiants AWS, et le même mécanisme couvre les téléchargements comme les envois.
Retour à l'entrepôt. Une presigned URL, c'est retrouver le coursier à la porte avec la caisse qu'il est venu chercher, au lieu de lui tendre le trousseau en comptant sur lui pour la trouver. Ce que le coursier emporte cesse de fonctionner quand sa pendule est écoulée.
Pour le cas où chaque personne connectée atteint ses propres fichiers, AWS
renvoie encore ailleurs : ses consignes sur la sécurisation des clés d'accès
disent que les applications mobiles devraient utiliser Amazon Cognito pour
gérer les identités au lieu d'intégrer des clés d'accès. Ce que Cognito donne
au navigateur est un identifiant temporaire, la sorte ASIA du début de cet
article, qui cesse de fonctionner tout seul au bout d'un moment. Une copie
raclée sur votre page un mardi est déjà morte le mercredi, et c'est la seule
propriété qu'une clé AKIA n'a pas.
Comment vérifier si mon application livre une clé AWS ?
Cherchez dans ce que le navigateur a téléchargé, pas dans ce qui est dans votre projet. Ce sont deux fichiers différents, et celui qui compte est celui que vos visiteurs reçoivent.
Dans le navigateur. Ouvrez votre site publié, appuyez sur F12, et cherchez
AKIA dans le JavaScript chargé. Chrome et Edge cherchent dans tous les
fichiers chargés d'un coup depuis le panneau Sources.
Dans votre projet. Cherchez AKIA, AWS_ACCESS_KEY_ID et
AWS_SECRET_ACCESS_KEY. Cherchez ensuite les deux préfixes qui mettent une
valeur dans un bundle exprès, VITE_ et NEXT_PUBLIC_, et lisez chaque valeur
derrière eux.
Pourquoi ces deux préfixes publient tout ce qu'ils nomment
est la mécanique derrière la plupart de ces cas, et c'est une règle du build
que personne n'a tapée par erreur.
De l'extérieur. Notre scan gratuit lit votre site publié comme le ferait un inconnu et signale quels identifiants il voit dans ce que vous avez livré, celui-ci compris. Il prend environ 20 secondes et ne demande aucun compte : scanner votre application gratuitement.
Sur toutes les applications que nous avons scannées, c'est une trouvaille rare : une clé d'accès AWS est apparue dans 9 des 31 056 applications où ce contrôle est allé au bout. Celles qui reviennent bien plus souvent sont les clés Google, en général sans problème, et celles d'OpenAI, en général avec. Ce qui fuit vraiment des applications construites ainsi les met toutes côte à côte avec les chiffres.
Ce que la clé a fait pendant qu'elle était dehors
Ouvrez l'historique des événements CloudTrail et filtrez sur l'identifiant de la clé d'accès.
L'historique des événements est actif dans chaque compte AWS sans rien à allumer, il garde les 90 derniers jours, et il ne coûte rien de plus. Chaque entrée porte l'identifiant de la clé qui a fait l'appel, vous pouvez donc relire ce que votre clé a fait. Cela répond aux questions de configuration : un bucket créé, une politique changée, un utilisateur ajouté.
Puis il y a la partie qu'il vaut mieux connaître avant de chercher. L'historique garde les événements de management. Lire un objet dans S3 est un événement de data, et AWS dit explicitement que les trails ne les journalisent pas par défaut et que les événements de data n'apparaissent pas du tout dans l'historique. Les allumer enregistre à partir de cet instant.
La lecture honnête est donc celle-ci. Si la clé a servi à modifier votre compte, le journal vous le montre. Si elle a servi à télécharger le contenu d'un bucket, il ne le montrera probablement pas, et vous décidez quoi dire à vos clients en sachant que c'est peut-être arrivé.
Garder une clé hors du prochain build
Un identifiant part vers le navigateur au moment du build, donc le prochain déploiement est celui à surveiller. La clé que vous retirez aujourd'hui revient l'après-midi où quelqu'un ajoute une fonctionnalité qui a besoin de S3 et demande à un agent de la câbler.
Reeve Monitor relit votre site publié pour vous :
- les neuf contrôles toutes les heures, sur jusqu'à trois applications
- un message quand un résultat change, pour qu'une clé arrivée un mardi n'attende pas votre prochain coup d'œil
- si l'application répond, toutes les 60 secondes
- un rapport mensuel de ce qui a été vu
Si votre application garde ses données dans un projet Supabase, Reeve Care en garde aussi une copie :
- une copie chiffrée de votre base Supabase chaque nuit, rangée là où votre projet ne l'atteint pas
- 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 envoyés aussi, dès que vous connectez un identifiant Storage
- tout ce que fait Monitor
Chacun de ces neuf contrôles décrit ce qu'un inconnu peut atteindre. Une copie de la base d'hier soir répond à l'autre question, celle de ce que quelqu'un a déjà modifié pendant que la clé était dehors.
Quoi faire cette semaine
Que faire
- Ouvrez votre site publié, appuyez sur F12, et cherchez
AKIAdans le JavaScript chargé. C'est le fichier que vos visiteurs ont, et le seul qui compte. - Si vous en trouvez une, désactivez-la dans IAM aujourd'hui. Désactiver est réversible et prend effet tout de suite ; supprimer ne l'est pas, et la moitié secrète ne se récupère pas.
- Lisez les politiques attachées à l'utilisateur de la clé avant de juger de la gravité. Ce qu'elle peut lire, ce qu'elle peut écrire et ce qu'elle peut démarrer sont trois réponses distinctes.
- Remplacez le schéma au lieu de la valeur. Votre serveur garde la clé et signe une presigned URL pour un objet, ou Amazon Cognito donne au navigateur un identifiant temporaire qui expire tout seul.
- Filtrez l'historique des événements CloudTrail sur la clé pour les 90 derniers jours. Il vous montrera ce que la clé a configuré, et il ne vous montrera pas quels fichiers elle a lus si les événements de data n'étaient pas déjà allumés.
Commencez par la recherche F12, car elle règle en une minute la question de savoir si vous avez un problème. Si elle sort quelque chose, la checklist de sécurité en 10 minutes couvre le reste de ce qu'une application fraîchement lancée a tendance à laisser ouvert, et déplacer un secret dans une edge function est le schéma pour chaque clé qui n'aurait jamais dû être dans la page.
FAQ
Est-il parfois acceptable de mettre des clés AWS dans le code frontend ?
Non. AWS le dit dans son propre guide d'audit de sécurité, à propos des applications mobiles : une application ne devrait jamais contenir de clés d'accès intégrées, même chiffrées. Un bundle de navigateur se lit plus facilement qu'un binaire d'application, donc la même réponse vaut ici et coûte moins de travail à un inconnu. Si votre page doit vraiment atteindre S3, les voies prévues sont une presigned URL signée par votre propre serveur, ou des identifiants temporaires d'Amazon Cognito, qui expirent d'eux-mêmes.
Que peut faire quelqu'un avec ma clé d'accès AWS ?
Exactement ce que ses permissions autorisent, ce qui explique qu'il n'y ait pas une seule réponse. Une clé attachée à un utilisateur avec accès en lecture à un bucket lit ce bucket. Une clé attachée à un administrateur fait tout ce que fait un administrateur, y compris démarrer des machines facturées à l'heure. Une clé du compte root est le pire cas, car AWS écrit que l'utilisateur root a un accès sans restriction à tous les services, ressources et données de facturation. Ouvrez IAM et lisez les politiques attachées à la clé avant de décider à quel point vous inquiéter.
Qu'est-ce qu'une presigned URL ?
Une longue URL qui permet à un navigateur de faire une seule chose à un seul fichier dans S3, pendant un nombre de secondes fixé, sans détenir le moindre identifiant AWS. Votre propre serveur garde la clé et signe l'URL à la demande. AWS décrit les presigned URLs comme un accès temporaire aux objets S3 sans identifiants AWS, et elles marchent aussi bien pour les téléchargements que pour les envois.
Comment vérifier si mon application livre une clé AWS ?
Cherchez dans ce que votre navigateur a téléchargé, pas dans ce qui est dans votre projet. Ouvrez votre site publié, appuyez sur F12, et cherchez AKIA dans le JavaScript chargé. Cherchez ensuite dans votre projet AKIA, AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY, et tout nom de variable commençant par VITE_ ou NEXT_PUBLIC_ qui en contient une, car ce sont ces préfixes qui mettent une valeur dans le bundle au départ.
Ma clé AWS est sur GitHub. Et maintenant ?
Désactivez-la d'abord dans IAM, ce qui est réversible et prend effet tout de suite, puis créez un remplacement pour le serveur qui en avait besoin. La retirer de votre code ne ferme rien, car l'ancienne valeur reste dans votre historique de commits et dans chaque clone que quelqu'un a fait. Ouvrez ensuite l'historique des événements CloudTrail et filtrez sur la clé pour voir ce qu'elle a fait.