When we scan apps built with AI coding tools, one vulnerability appears in over 60% of them: exposed API keys in frontend JavaScript.
This isn't a theoretical risk. Exposed keys lead to real consequences: unauthorized database access, runaway cloud bills, data breaches, and account takeovers. And it's happening at scale because AI coding tools embed secrets in client-side code by default.
Why AI Tools Expose Keys
When you prompt Lovable to "add Supabase auth" or tell Cursor to "connect to my Firebase project," the AI does what you asked — it writes working code. But "working" doesn't mean "secure."
Here's what typically happens:
- The AI puts keys where the code needs them — usually in a frontend config file or directly in component code
- The app works, so the developer ships it
- The keys are now in the JavaScript bundle, visible to anyone who opens DevTools
What Keys Get Exposed?
We categorize exposed keys by severity:
Critical Immediate Action Required
High Should Fix Soon
Low Risk Generally Safe in Frontend
How to Find Exposed Keys
Manual Check
Open your deployed app, then:
- Open DevTools (F12)
- Go to Sources tab
- Search (Ctrl+Shift+F) for patterns like:
If you find any critical keys, rotate them immediately.
Automated Check
ScanVibe's secrets analyzer scans your app's JavaScript bundles, HTML source, and network requests for 30+ patterns of exposed credentials. One scan covers everything.
How to Fix It
The fix depends on your framework, but the principle is always the same: secrets go on the server, never in the browser.
Next.js
# .env.local
# PUBLIC — safe for browser (no secret access)
NEXT_PUBLIC_SUPABASE_URL=https://abc.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhb...public
# PRIVATE — server-side only
SUPABASE_SERVICE_ROLE_KEY=eyJhb...secret
STRIPE_SECRET_KEY=sk_live_...
OPENAI_API_KEY=sk-...
NEXT_PUBLIC_ are included in the browser bundle. Everything else stays server-side.For API Calls That Need Secret Keys
Create a server-side API route:
// app/api/generate/route.ts (server-side)
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY, // Never in browser
});
export async function POST(request: Request) {
const { prompt } = await request.json();
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
});
return Response.json({ result: response.choices[0].message.content });
}
Then call it from your frontend:
// Client-side — no API key needed
const response = await fetch('/api/generate', {
method: 'POST',
body: JSON.stringify({ prompt: 'Hello' }),
});
Vite / React (without Next.js)
# Only VITE_ prefix vars go to the browser
VITE_SUPABASE_URL=https://abc.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhb...public
# These stay server-side (use in a backend)
SUPABASE_SERVICE_ROLE_KEY=eyJhb...secret
What To Do If Your Keys Are Already Exposed
If you've found exposed keys in production:
- Rotate the key immediately. Generate a new one in the service's dashboard.
- Check for unauthorized usage. Look at billing dashboards, API logs, and database audit logs.
- Update your code to use the new key server-side only.
- Check git history. If the key was ever committed to a repo, consider the old key permanently compromised — even if you removed it in a later commit.
- Use
.gitignoreto exclude.envfiles from version control.
Where to Rotate Keys
Prevention: Stop It Before It Ships
1. Use ScanVibe to scan every deployment. We catch exposed keys before attackers do.
2. Add a pre-commit hook with tools like
gitleaks or trufflehog to catch secrets in commits.3. Review AI-generated code before shipping. Check for hardcoded strings that look like keys.
4. Educate your team on the difference between public and secret keys.
5. Use environment variable validation to ensure server-side vars are set.
// lib/env.ts
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing env var: ${name}`);
return value;
}
export const env = {
stripeSecret: requireEnv('STRIPE_SECRET_KEY'),
supabaseServiceRole: requireEnv('SUPABASE_SERVICE_ROLE_KEY'),
};
The Bottom Line
AI coding tools are incredible for shipping fast. But they don't think about security — they think about making your code work. The #1 thing you can do to secure your AI-built app is to make sure your secrets stay on the server.
FAQ
Can a no-code platform scan my app for exposed API keys before I launch?
Yes. ScanVibe is a free security scanner that needs no code and no installation: paste your app's URL and it analyzes the HTML and JavaScript your app sends to the browser, looking for secret key patterns such as Stripe sk_live_, Supabase service_role, OpenAI sk- or AWS AKIA. It also checks whether files like /.env or /.git/config are publicly reachable. Run it on your preview or staging URL before launch, then again after each deployment.
How do I protect a Stripe sk_live_ key from being exposed?
Only the publishable key (pk_live_) belongs in the frontend. The secret key sk_live_ must only be read on the server, in an API route, an Edge Function or a backend:
- store it in an environment variable without a public prefix (never
NEXT_PUBLIC_STRIPE_SECRET_KEYorVITE_STRIPE_SECRET_KEY); - keep
.envfiles out of git with.gitignore; - where possible, use a Stripe restricted key (
rk_live_) limited to the permissions your server actually needs; - if the key was ever exposed, roll it in Developers → API keys in the Stripe dashboard.
What to do if my OPENAI_API_KEY was exposed in a .env file?
Treat the key as compromised, even if you deleted the file afterwards:
- Revoke it in the OpenAI dashboard (API keys) and create a new one.
- Check your usage and billing for requests you didn't make.
- Find how it leaked. A committed
.envstays in git history even after you remove it in a later commit; a.envfile served athttps://your-app.com/.envis readable by anyone; a frontend variable likeNEXT_PUBLIC_OPENAI_API_KEYorVITE_OPENAI_API_KEYis shipped in your JavaScript bundle. - Put the new key server-side only and call OpenAI through your own API route, as shown above.
- Re-scan your app to confirm the key and the
.envfile are no longer exposed.