The LLM Key Leak Epidemic
LLM API keys are among the most frequently leaked secrets today. OpenAI, Anthropic, Google, and Cohere keys are found in public GitHub repositories, client-side JavaScript, mobile app binaries, and browser network requests daily. The financial impact is significant -- leaked keys are immediately exploited by attackers who run massive inference workloads, generating bills of thousands of dollars within hours.
How Keys Get Leaked
1. Hardcoded in Frontend Code
// BAD: Visible in browser source and network requests
const openai = new OpenAI({
apiKey: 'sk-proj-abc123...',
dangerouslyAllowBrowser: true, // The name says it all
});
The dangerouslyAllowBrowser flag exists for prototyping only. Any key used with this flag will be extracted within minutes of deployment.
2. Committed to Git
# These are in your git history forever, even after deletion
$ git log --all -p -- .env
$ git log --all -p -- '*.ts' | grep "sk-"
Even if you delete the file containing the key, it remains in git history. Secret scanning tools run by attackers specifically target git history.
3. Embedded in Mobile Apps
// BAD: Extractable from the APK
class AIService {
private val apiKey = "sk-ant-api03-..."
suspend fun complete(prompt: String): String {
// Direct API call with embedded key
}
}
4. Exposed in Environment Variables
# .env file committed to repo
OPENAI_API_KEY=sk-proj-abc123
ANTHROPIC_API_KEY=sk-ant-api03-xyz789
# Or logged to console
console.log('Config:', process.env); // Leaks all env vars
5. Client-Side API Calls
Even without hardcoding the key, making direct LLM API calls from the client exposes the key in network requests:
// BAD: Key visible in browser DevTools Network tab
fetch('https://api.openai.com/v1/chat/completions', {
headers: { 'Authorization': 'Bearer sk-proj-abc123...' },
// ...
});
Secure Architecture
Use a Server-Side Proxy
The correct architecture routes all LLM requests through your backend:
Client App --> Your Backend/Edge Function --> LLM API
(key stored here)
// Supabase Edge Function as an LLM proxy
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
Deno.serve(async (req: Request) => {
// 1. Authenticate the user
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!,
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } }
);
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return new Response('Unauthorized', { status: 401 });
}
// 2. Check rate limits and credits
const adminClient = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
const { data: profile } = await adminClient
.from('profiles')
.select('credits')
.eq('id', user.id)
.single();
if (!profile || profile.credits < 1) {
return new Response('Insufficient credits', { status: 403 });
}
// 3. Make the LLM call server-side
const { prompt } = await req.json();
const llmResponse = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'x-api-key': Deno.env.get('ANTHROPIC_API_KEY')!, // Server-side only
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
},
body: JSON.stringify({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }],
}),
});
// 4. Deduct credit
await adminClient
.from('profiles')
.update({ credits: profile.credits - 1 })
.eq('id', user.id);
return new Response(llmResponse.body, {
headers: { 'Content-Type': 'application/json' },
});
});
Set Spending Limits
Every LLM provider offers spending limits or usage caps. Configure them:
- OpenAI: Set monthly spending limits in the billing dashboard
- Anthropic: Configure spend limits in the console
- Google AI: Set quota limits per project
Even with perfect security, set limits as a safety net.
Rotate Keys Regularly
Treat LLM API keys like passwords:
- Rotate keys on a regular schedule (monthly or quarterly)
- Rotate immediately if there is any suspicion of compromise
- Use separate keys for development and production
- Use separate keys for different services or environments
Use Scoped Keys When Available
Some providers offer scoped or restricted keys:
- Use project-scoped keys instead of organization-wide keys
- Restrict key permissions to only the models and endpoints needed
- Use separate keys for different applications
Git Protection
Pre-commit Hooks
Use tools like gitleaks or detect-secrets to catch keys before they are committed:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
.gitignore
Ensure environment files are ignored:
# .gitignore
.env
.env.local
.env.production
*.key
*.pem
GitHub Secret Scanning
Enable GitHub's secret scanning and push protection on your repositories. GitHub partners with OpenAI, Google, and other providers to automatically revoke leaked keys.
What To Do If a Key Is Leaked
- Revoke the key immediately -- Do not wait. Generate a new key first, update your servers, then revoke the old one.
- Check usage logs -- Most providers show API usage. Check for unauthorized calls.
- Audit git history -- If the key was committed, it is in the history forever. Consider using
git-filter-repoor BFG Repo-Cleaner to remove it. - Rotate all related credentials -- If one key was leaked, assume other secrets in the same environment may also be compromised.
Automated Detection
AuditYour.app scans your applications for exposed LLM API keys in frontend code, mobile binaries, and public configurations. Catch leaks before attackers do.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free ScanRelated
guides
Securing API Keys in Mobile Applications
Techniques for protecting secrets in mobile binaries
guides
APK Reverse Engineering & Security Analysis
How attackers extract secrets from Android apps and how to defend
guides
BaaS Security Architecture Guide
Architectural patterns for securing backend-as-a-service applications
guides
Hardening Supabase Edge Functions
Best practices for secure Edge Function development