“Scotcher la clé de votre maison sur la porte d'entrée parce que vous étiez pressé.”
Le code client n'a pas de secrets
Tout ce que votre navigateur exécute, votre utilisateur peut le lire. Le bundle JavaScript est téléchargé sur sa machine ; le minifier n'est pas le chiffrer. Tout ce que vous mettez dans le code front-end (une clé dans une variable, un token dans une config, un secret dans une variable d'environnement exposée au client) est visible par quiconque ouvre les outils développeur. Il n'y a pas de « caché » dans le navigateur.
Cela piège les gens parce que les frameworks ont une convention qui ressemble à une fonctionnalité de sécurité mais n'en est pas une. Un préfixe comme NEXT_PUBLIC_ (ou VITE_, ou REACT_APP_) ne protège pas une valeur ; il fait l'inverse. Il marque la variable comme devant être intégrée au bundle client et livrée au navigateur. Le préfixe signifie « ceci est public ». Un outil IA qui s'en sert pour « corriger » une variable indéfinie publie discrètement votre secret.
Deux types de clés, et la confusion fatale
La plupart des services qui comptent vous donnent deux types d'identifiants, et tout le jeu consiste à ne pas les confondre :
- Clés publiables / anon / client : conçues pour être publiques. La clé publiable de Stripe, la clé anon de Supabase. Sûres dans le navigateur car, seules, elles ne peuvent rien faire de privilégié (elles s'appuient sur des règles serveur comme la RLS pour être sûres).
- Clés secrètes / service / serveur : pleins pouvoirs. La clé secrète de Stripe, une clé OpenAI, une clé service-role de base de données. Elles peuvent débiter des cartes, dépenser votre budget de modèle, ou lire chaque ligne. Elles ne doivent jamais toucher le navigateur, un dépôt public ou un prompt.
Déjà fuitée ? Faites-la tourner : on ne peut pas dé-fuiter.
Si un secret a été dans du code client, un dépôt public, un log ou un chat, il est grillé. Supprimer la ligne n'aide pas : il est toujours dans l'historique git, dans le cache de quelqu'un, dans la base d'un scraper. Le seul vrai correctif est la rotation : révoquer l'ancienne clé chez le fournisseur, en émettre une nouvelle, puis stocker la nouvelle correctement. Cacher une clé fuitée n'est pas une remédiation ; la faire tourner, si.
# .gitignore: keep secrets out of the repo in the first place .env .env.local .env.*.local # Already committed once? It's in history. Rotate the key at the provider, # then scan history so you know what else escaped: # npx gitleaks detect --source . --redact
Où les clés doivent vivre
Le schéma qui tient : les secrets vivent sur le serveur, et le navigateur demande au serveur de les utiliser en son nom. Au lieu que le navigateur appelle une API tierce avec une clé secrète, il appelle votre propre endpoint, et votre serveur, que l'utilisateur ne peut pas lire, ajoute la clé et effectue l'appel. La clé ne franchit jamais la frontière vers du code que l'utilisateur contrôle.
- Variables d'environnement serveur ou gestionnaire de secrets : jamais un fichier dans le dépôt, jamais un préfixe exposé au client.
- Une route serveur légère qui proxifie les API tierces, pour que la clé secrète reste côté serveur (le parti pris de SDEN pour de vrais backends plutôt que des stacks purement client vient en partie de là).
- Jamais dans le bundle du navigateur, jamais dans git, jamais collée dans un prompt IA ou un document partagé.
Une fois que vous avez intériorisé « le navigateur est public », la plupart des décisions de gestion des secrets se répondent d'elles-mêmes : si la fuite d'une valeur ferait mal, elle va sur le serveur, point final.
En une ligne chacun
- Tout ce qui est dans le navigateur est lisible ; un préfixe NEXT_PUBLIC_/VITE_ publie une valeur, il ne la protège pas.
- Les clés publiables sont sûres côté client par conception ; les clés secrètes/service (Stripe, OpenAI, service-role) ne le sont jamais.
- Un secret fuité ne peut pas être dé-fuité : faites-le tourner chez le fournisseur ; supprimer la ligne le laisse dans l'historique git et chez les scrapers.
- Les clés appartiennent au serveur : env/gestionnaire de secrets plus un proxy léger, jamais dans le bundle, git ou un prompt.
Où aller ensuite