Exposed API Keys: The #1 Vulnerability in AI-Built Apps
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.