En mars 2024, GitGuardian a rapporté que plus de 12,8 millions de nouveaux secrets avaient fuité dans des dépôts GitHub publics en 2023, soit une hausse de 28% par rapport à l'année précédente (State of Secrets Sprawl 2024). Ce dont on parle moins, c'est du nombre de ces secrets qui finissent non pas dans le code source, mais dans le HTML et le JavaScript servis aux visiteurs.
On scanne des sites e-commerce au quotidien. On tombe régulièrement sur des clés AWS dans des bundles JavaScript, des clés Stripe live sur des pages de paiement, des tokens API codés en dur dans des scripts inline. Le tout visible par n'importe qui ouvrant les DevTools.
Comment des secrets finissent en production
Personne ne met une clé AWS dans son HTML exprès. Mais le chemin entre "raccourci temporaire" et "fuite en production" est plus court qu'on ne le croit.
Mauvaise configuration des outils de build : c'est la cause la plus fréquente. Webpack, Vite et Next.js gèrent les variables d'environnement différemment. Dans Next.js, toute variable préfixée par NEXT_PUBLIC_ est intégrée dans le bundle côté client. C'est voulu. Mais des développeurs qui ne comprennent pas cette distinction préfixent parfois des clés sensibles de la même façon. Vite fonctionne pareil avec le préfixe VITE_ (documentation Vite).
Copier-coller depuis la documentation. Les docs des passerelles de paiement et des SDK analytics fournissent des snippets avec des clés placeholder. Un développeur remplace le placeholder par une vraie clé "pour tester vite", et ce code de test part en production. Ça arrive surtout sur les plateformes où on travaille directement dans l'éditeur de thème (Shopify Liquid, fichiers de thème WordPress) sans pipeline de déploiement.
Rendu côté serveur qui dérape. Dans les frameworks SSR (Nuxt.js, Next.js, SvelteKit), la frontière entre code serveur et code client est floue. Une variable dans une fonction côté serveur peut se retrouver sérialisée dans l'état initial de la page ou le payload d'hydratation. La clé n'apparaît jamais dans le code source directement, mais elle est bien là dans le blob JSON __NEXT_DATA__ ou __NUXT__ embarqué dans le HTML.
Les 12 patterns de secrets détectés par PurpleScan
PurpleScan scanne l'intégralité du code source de la page (HTML, scripts inline, fichiers JavaScript liés) avec des expressions régulières ciblant les types de credentials les plus dangereux dans un contexte web.
Clés de fournisseurs cloud
AWS Access Key IDs (
AKIA[0-9A-Z]{16}). Ces chaînes de 20 caractères commençant par "AKIA" sont reconnaissables au premier coup d'œil. Si elles sont trouvées avec la clé secrète correspondante, un attaquant a un accès programmatique complet à votre compte AWS. En 2023, Sysdig a documenté des attaquants qui trouvaient des clés AWS exposées et lançaient des instances de crypto-mining en quelques minutes.Clés API Google Cloud (
AIza[0-9A-Za-z\-_]{35}). Souvent fuitées via les intégrations Google Maps. Les clés API Maps sont conçues pour être publiques (avec des restrictions de domaine), mais le même format est utilisé pour des services Google Cloud plus sensibles.
Paiement et finance
Clés secrètes Stripe live (
sk_live_[0-9a-zA-Z]{24,}). Le pire scénario pour un site e-commerce. Une clé secrète Stripe qui fuite permet à un attaquant d'émettre des remboursements, de créer des paiements et d'accéder à l'historique complet des transactions de vos clients. Stripe prévient explicitement que les clés secrètes ne doivent jamais apparaître dans du code côté client.Clés publiques Stripe (
pk_live_[0-9a-zA-Z]{24,}). Celles-ci sont conçues pour être publiques. PurpleScan les signale en INFO parce que leur présence confirme l'intégration Stripe et aide à vérifier que la clé secrète n'a pas été confondue avec la clé publique.
Tokens d'authentification
GitHub Personal Access Tokens (
ghp_[0-9a-zA-Z]{36}). Plus fréquents qu'on ne le pense sur les sites e-commerce qui récupèrent du contenu depuis GitHub (architectures headless CMS, documentation). Un token GitHub fine-grained donne un accès lecture/écriture au dépôt.Tokens JWT (
eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_.+/]*). Encodés en base64 et faciles à repérer. Le payload (section du milieu) n'est pas chiffré. N'importe qui peut le décoder et lire les claims qu'il contient : identifiants utilisateur, emails, rôles. Si c'est un token admin de longue durée, c'est un bypass d'authentification direct.
Infrastructure et clés privées
Clés privées RSA/PEM (
-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----). Il n'y a jamais de raison légitime pour qu'une clé privée apparaisse dans du code côté client. C'est en général une erreur de configuration grave : la clé privée d'un certificat SSL, une clé SSH ou une clé de signature API exposée à tout le monde.Clés API génériques : chaînes à haute entropie proches de mots-clés comme
api_key,apiKey,secret,token,passworddans des assignations de variables JavaScript.
Ce qui se passe quand une clé Stripe fuite
Concrètement :
Un attaquant visite votre page de paiement et ouvre les DevTools
Il cherche
sk_livedans le code source et trouve votre clé secrète StripeAvec le CLI Stripe ou une commande curl, il peut lister tous vos clients (noms, emails, moyens de paiement), émettre des remboursements sur des charges légitimes, facturer des moyens de paiement enregistrés, et consulter l'historique complet de vos transactions
L'API Stripe donne un accès complet avec une clé secrète. Pas d'authentification secondaire par défaut.
Pas d'injection SQL, pas de compromission serveur. Un navigateur et 30 secondes.
Les requêtes réseau aussi font fuiter des secrets
PurpleScan ne se limite pas au HTML et au JavaScript. Pendant le crawl, on capture chaque requête réseau émise par la page et on inspecte les URLs et les en-têtes à la recherche de patterns de credentials. Ça attrape les secrets dans :
Les appels API depuis le navigateur :
fetch('https://api.example.com/data?api_key=sk_live_xxx')Les paramètres d'URL sur les pixels de tracking qui incluent des tokens
Les en-têtes Authorization, visibles dans les DevTools et parfois loggés par les fournisseurs CDN
Prévention : garder les secrets hors du navigateur
1. Variables d'environnement, correctement
Chaque framework moderne distingue les variables d'environnement côté serveur et côté client. Sachez où est la frontière :
Next.js : seules les variables
NEXT_PUBLIC_*sont exposées au navigateur. Gardez les secrets sans ce préfixe.Vite : seules les variables
VITE_*sont exposées. Le reste reste côté serveur.Nuxt 3 : utilisez
runtimeConfigpour les valeurs serveur uniquement,runtimeConfig.publicpour les valeurs client.
2. Proxifier les appels API sensibles
Si votre frontend a besoin de données d'une API tierce qui nécessite une authentification, ne l'appelez pas directement depuis le navigateur. Mettez en place une route côté serveur (API route dans Next.js, server middleware dans Nuxt) qui fait l'appel authentifié et renvoie les données. Le secret ne quitte jamais votre serveur.
3. Scan de secrets dans le CI/CD
Ajoutez de la détection automatique de secrets à votre pipeline de déploiement. TruffleHog, BetterLeaks ou le secret scanning intégré de GitHub peuvent attraper les fuites avant la mise en production. Mais ces outils scannent votre code source. Ils ne détecteront pas les secrets qui apparaissent dans le rendu final à cause d'une mauvaise configuration de build, c'est là que PurpleScan intervient.
4. Rotation immédiate des clés compromises
Si PurpleScan (ou n'importe quel outil) trouve un secret exposé, la rotation de la clé n'est pas optionnelle et ne se planifie pas pour le prochain sprint. La clé est compromise depuis le moment où elle a été déployée. Faites la rotation, vérifiez les logs d'accès pour détecter une utilisation non autorisée, puis corrigez la cause racine.
Plus d'intégrations, plus de risques
Passerelles de paiement, analytics, connexions CRM, headless CMS, APIs d'inventaire : chaque intégration vient avec des credentials qui demandent de l'attention. PurpleScan lance ces 12 vérifications de patterns à chaque scan, sur chaque page. Une clé invisible sur votre homepage peut fuiter sur votre page de paiement ou un environnement de staging accidentellement public.
La prochaine fois que vous déployez, ouvrez les DevTools sur votre propre site et cherchez sk_live, AKIA ou ghp_. Vous n'aimerez peut-être pas ce que vous trouverez.