Scanner de clés d'API exposées

Tout ce qui est dans votre bundle est public.

Saisissez une URL et nous récupérons le JavaScript que votre page demande au navigateur de charger, puis nous le lisons à la recherche d'identifiants. Pas une supposition tirée du HTML : les vrais fichiers de scripts, ceux-là mêmes que n'importe qui ouvre dans les devtools.

Examinez des sites qui vous appartiennent ou que vous êtes autorisé à tester. Nous demandons la page comme le ferait un navigateur et lisons ce qui revient. Consultez nos conditions.

Ce que cela vérifie, et à quoi ressemble une bonne réponse

Clés service_role de Supabase

Le pire de cette liste. La clé service_role contourne chaque politique de row level security que vous avez écrite : celui qui la détient peut lire, modifier et supprimer chaque ligne de chaque table, quel que soit le compte avec lequel il est connecté. Elle est visuellement identique à la clé publiable anon, et c'est pour cela qu'elle finit dans le code client : quelqu'un a rencontré une erreur de permission, a échangé une clé contre l'autre, l'erreur a disparu, et c'est parti en production.

Clés anon de Supabase et configuration Firebase

Celles-ci sont faites pour être publiques, et les trouver n'est pas un constat en soi. Nous les signalons parce que leur valeur dépend entièrement de vos règles : une clé anon avec le row level security désactivé lit toute votre base, et une configuration Firebase dont les règles sont restées à allow read, write: if true fait la même chose. La clé n'est pas la vulnérabilité. La clé plus une règle absente, si.

Clés secrètes Stripe

Une clé sk_live_ dans du code client signifie que n'importe qui peut créer des paiements, émettre des remboursements et lire votre liste de clients. Nous signalons aussi les clés de test, à une sévérité plus basse : une sk_test_ dans le bundle ne coûte pas d'argent, mais elle indique que la publiable et la secrète ont été confondues une fois, et la même erreur finit généralement par atteindre la production.

Clés d'accès AWS, tokens GitHub et clés privées

Une paire de clés AWS dans un bundle, c'est une facture et une compromission en même temps, et ce sont les identifiants que les collecteurs automatiques traquent le plus. Un token GitHub expose les sources et, s'il porte un droit d'écriture, la possibilité de pousser. Un bloc BEGIN PRIVATE KEY dans du code client n'a aucune raison légitime de s'y trouver.

Clés de fournisseurs de modèles

Les clés OpenAI, Anthropic et assimilées dans du code client sont l'entrée la plus récente de cette liste et de plus en plus la plus fréquente, parce qu'appeler le modèle directement depuis le navigateur est le chemin le plus court vers une démo qui marche. C'est aussi un identifiant facturé à l'usage que n'importe qui peut dépenser. C'est la défaillance qui se transforme en facture à quatre chiffres du jour au lendemain plutôt qu'en fuite de données.

Tokens d'autorisation écrits en dur

Une valeur littérale Authorization: Bearer ... figée dans le bundle, généralement oubliée après un test contre un endpoint réel. Elle accorde tout ce que le token accorde, à tout le monde, tant qu'elle reste valide.

Chaînes de connexion à la base de données

Une URL postgres:// ou mongodb+srv:// avec son mot de passe dedans, posée dans un fichier que chaque visiteur télécharge. C'est la version la plus directe de tout le problème : elle n'est limitée à aucun utilisateur, aucune politique ne se tient derrière, et quiconque la copie peut lire toutes les tables, modifier toutes les lignes et tout supprimer. Nous ne vous renvoyons jamais le mot de passe, seulement l'hôte, pour que vous sachiez de quelle base il s'agit. Les exemples de tutoriel comme postgres://user:password@localhost sont reconnus et écartés, ce qui explique que documenter votre configuration sur votre propre site ne déclenche pas cette alerte.

Slack, SendGrid, Twilio, npm, Resend, Mapbox et Algolia

La longue traîne, et la raison pour laquelle un scanner qui ne connaît que les cinq célèbres passe à côté de tant de choses. Un token de bot Slack lit vos canaux privés. Une clé SendGrid ou Resend envoie du courrier avec la réputation de votre domaine derrière, et c'est pourquoi celles qui fuitent deviennent du phishing en quelques heures. Un token de publication npm peut expédier du code à tous ceux qui vous installent. Deux de ces alertes sont volontairement classées plus bas : une URL de webhook Slack n'écrit que dans un seul canal, et un identifiant de compte Twilio seul n'ouvre rien, donc aucune des deux n'est déguisée en fuite. La vérification Algolia est formulée comme une question et non comme un verdict, parce que la clé de recherche et la clé d'administration sont des chaînes identiques et que les distinguer supposerait d'utiliser la clé, ce qu'un scan passif ne fait pas.

Comment corriger

Il n'existe qu'un seul vrai correctif, et il a deux moitiés : faites tourner l'identifiant, parce que tout ce qui a atteint un navigateur doit être considéré comme étant déjà entre d'autres mains, puis déplacez côté serveur l'appel qui en avait besoin. La rotation n'est ni optionnelle ni à remettre à plus tard. Les clés présentes dans les bundles publics sont trouvées par des scanners automatiques en quelques heures.

Les noms de variables qui fuitent
# Anything with these prefixes is INLINED INTO THE BUNDLE at build
# time. Not read at runtime, not kept on the server: pasted into a
# file anyone can download. A secret here is a published secret.

NEXT_PUBLIC_*      # Next.js
VITE_*             # Vite, so also most Lovable and Bolt output
PUBLIC_*           # Astro, SvelteKit
REACT_APP_*        # Create React App
EXPO_PUBLIC_*      # Expo

# Server-only names have no prefix, and your host's secret store is
# where they belong:
SUPABASE_SERVICE_ROLE_KEY=...
STRIPE_SECRET_KEY=...
OPENAI_API_KEY=...
Déplacer l'appel côté serveur (Next.js)
// app/api/summarise/route.ts
// The key stays on the server. The browser calls YOUR route, which
// is also the only place you can enforce who is allowed to ask.
import { NextResponse } from "next/server";

export async function POST(request: Request) {
  const session = await auth();
  if (!session) return NextResponse.json({ error: "sign in" }, { status: 401 });

  const answer = await fetch("https://api.openai.com/v1/responses", {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({ model: "gpt-5.4-mini", input: await request.text() }),
  });

  return NextResponse.json(await answer.json());
}
Déplacer l'appel côté serveur (Supabase Edge Function)
// supabase/functions/admin-task/index.ts
// service_role belongs here and nowhere else. Deno.env reads the
// function's own secrets, which are never bundled to a client.
import { createClient } from "jsr:@supabase/supabase-js";

const admin = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);

Deno.serve(async (request) => {
  const token = request.headers.get("authorization");
  if (!token) return new Response("unauthorized", { status: 401 });
  // Check who is asking BEFORE using a key that ignores every policy.
  const { data: user } = await admin.auth.getUser(token.replace("Bearer ", ""));
  if (!user) return new Response("unauthorized", { status: 401 });

  return Response.json({ ok: true });
});
Faire tourner, dans l'ordre
1. Issue the new credential first, so nothing goes down.
2. Update your host's secret store and redeploy.
3. Revoke the old one. This is the step that actually helps, and
   the one people skip because everything already works without it.
4. Check the provider's audit log for use you cannot account for.
5. Search your git HISTORY, not just your files:
     git log -p -S 'sk_live_' -- . | head -50
   Deleting a key from a file leaves it in every clone of the repo.
Ce que cet outil ne peut pas voir

Ceci lit les scripts auxquels votre page renvoie, c'est-à-dire ce que voit le navigateur d'un visiteur, et rien de plus. Une clé qui n'apparaît que sur une route que nous n'avons pas chargée, dans un bundle derrière une authentification, ou dans votre historique git plutôt que dans votre build actuel, est invisible ici et tout aussi dangereuse. L'historique git en particulier est l'endroit où vivent réellement la plupart des clés fuitées : retirer un secret d'un fichier ne le retire pas du dépôt, et c'est l'un des premiers endroits où regarde une analyse du code lui-même.

Scanner aussi le codeGratuit pour démarrer. Connectez un dépôt et le premier scan tourne en deux minutes environ.