Why Exposed API Keys in Frontend Code Are Dangerous
Exposed API keys in frontend code are one of the most common and dangerous vulnerabilities in AI-built apps. Learn why they're dangerous, how attackers exploit them, and how to fix the problem in 5 minutes.
By Paula C · Kraftwire Software
· 9 min readKey Takeaway
API keys in frontend code are visible to everyone. If your React, Vue, or any other client-side app includes secret API keys, attackers can extract them in seconds. This guide explains the risk, shows you how to check, and walks through the fix.
The Problem with Frontend API Keys
Every line of JavaScript in your web application is sent to the user's browser. When someone visits your site, they download your entire frontend codebase. If that code contains an API key, the key is no longer secret.
This is not a theoretical risk. Automated tools scan public websites and GitHub repositories for exposed API keys around the clock. When they find one, the damage can happen within minutes: unauthorized API calls, data theft, or charges on your cloud account.
How Attackers Find Your Keys
It takes about 30 seconds. Open any website, press F12 to open DevTools, go to the Sources tab, and search for common key prefixes like "sk_", "api_key", or "secret". If a key is in your JavaScript bundle, it shows up in plain text.
Attackers also use automated scanners that crawl websites and extract strings matching known API key patterns. Services like Stripe, OpenAI, AWS, and Firebase all have recognizable key formats that are easy to detect programmatically.
The Financial Impact
Exposed API keys can lead to significant financial damage. OpenAI keys have been used to generate thousands of dollars in API charges within hours of exposure. AWS keys have been used to spin up cryptocurrency mining instances that cost tens of thousands of dollars. Stripe secret keys can be used to issue refunds, create charges, or access customer payment data.
The damage is not limited to direct financial loss. A data breach caused by an exposed API key can result in regulatory fines, legal liability, and loss of customer trust that takes years to rebuild.
Which Keys Are Safe in the Frontend
Not all API keys are secrets. Some are designed to be used in client-side code.
Publishable Keys (Safe for Frontend)
Stripe publishable key (starts with pk_)
Firebase config object (apiKey, authDomain, projectId)
Google Maps API key (when restricted by domain)
Analytics tracking IDs (Google Analytics, Mixpanel)
Public API keys with domain restrictions
These keys are designed to be public. They are restricted by domain, have limited permissions, or only identify your project without granting access to sensitive operations.
Secret Keys (Never Put in Frontend)
Stripe secret key (starts with sk_)
OpenAI API key (starts with sk-)
Database connection strings
AWS access keys
JWT signing secrets
Any key described as "secret" or "private" in the documentation
These keys grant full access to your account or data. If exposed, they can be used to make charges, access databases, or impersonate your application.
How to Check Your Code
Manual Search
Search your codebase for common patterns:
grep -r "sk_live" src/
grep -r "sk-" src/
grep -r "api_key" src/
grep -r "secret" src/
grep -r "password" src/
Check Your Environment Variables
In Vite projects, any environment variable starting with VITE_ is bundled into the frontend JavaScript. In Create React App, the prefix is REACT_APP_. These variables are NOT secret.
# These end up in the browser
VITE_STRIPE_SECRET_KEY=sk_live_... # WRONG
REACT_APP_DATABASE_URL=postgres://... # WRONG
# These stay on the server (no prefix)
STRIPE_SECRET_KEY=sk_live_... # Correct
DATABASE_URL=postgres://... # Correct
Check Your Built Output
Even if your source code looks clean, check the built JavaScript files. Sometimes build tools or plugins inject values you did not expect.
# Build your app
npm run build
# Search the output
grep -r "sk_" dist/
grep -r "secret" dist/
Check Your Git History
Even if your current code is clean, previous commits might contain exposed keys. Use git log to search through history:
git log -p --all -S "sk_live" -- "*.ts" "*.tsx" "*.js"
git log -p --all -S "sk-" -- "*.ts" "*.tsx" "*.js"
If you find keys in your git history, rotating them is essential. Anyone with access to your repository can browse historical commits and find previously committed secrets.
How to Fix Exposed Keys
Step 1: Rotate the Key Immediately
If a key has been exposed, assume it has been compromised. Go to the service provider's dashboard and generate a new key. Revoke the old one. Do not skip this step, even if you think nobody found it.
Step 2: Move the Key to the Server
Create a server-side function that makes the API call on behalf of the frontend. Your frontend calls your server, and your server uses the secret key.
// Frontend: call your own backend
const response = await fetch("/api/generate-text", {
method: "POST",
body: JSON.stringify({ prompt: userInput }),
});
// Backend (edge function): uses the secret key
const openai = new OpenAI({
apiKey: Deno.env.get("OPENAI_API_KEY"),
});
const completion = await openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: prompt }],
});
Step 3: Add Rate Limiting
Your server-side function is now a proxy. Without rate limiting, an attacker could still abuse your API quota by flooding your proxy endpoint. Add rate limiting based on IP address or authenticated user.
// Simple rate limiting example
const rateLimitMap = new Map();
function checkRateLimit(ip: string, maxRequests = 10, windowMs = 60000) {
const now = Date.now();
const userRequests = rateLimitMap.get(ip) || [];
const recentRequests = userRequests.filter(t => now - t < windowMs);
if (recentRequests.length >= maxRequests) {
return false; // Rate limited
}
recentRequests.push(now);
rateLimitMap.set(ip, recentRequests);
return true; // Allowed
}
Step 4: Check Your Git History
If the key was ever committed to your repository, it exists in your git history even after you remove it from the current code. Consider using tools like BFG Repo Cleaner to scrub sensitive data from your git history.
Step 5: Set Up Automated Detection
Add pre-commit hooks and CI/CD checks to prevent future key exposure:
# Install git-secrets
git secrets --install
git secrets --register-aws
# Add custom patterns
git secrets --add 'sk_live_[a-zA-Z0-9]{20,}'
git secrets --add 'sk-[a-zA-Z0-9]{20,}'
These hooks run before every commit and block any commit that contains strings matching known key patterns.
Prevention Best Practices
Use a Secrets Manager
Do not scatter API keys across .env files on different machines. Use a centralized secrets manager that your deployment pipeline pulls from. This way, keys never exist in your codebase or on developer laptops.
Implement Key Rotation
Rotate API keys on a regular schedule, not just when you suspect a breach. Many services support multiple active keys, so you can create a new key, update your server configuration, verify everything works, and then revoke the old key without any downtime.
Least Privilege Principle
When creating API keys, use the minimum permissions required. If your app only needs to read data, do not use a key with write access. If your app only needs access to one specific resource, do not use a key with full account access. Many services let you scope keys to specific operations.
Monitor API Usage
Set up alerts for unusual API usage patterns. If your OpenAI usage suddenly spikes from 100 requests per day to 10,000, something is wrong. Most API providers offer usage dashboards and alerting features that let you detect compromised keys quickly.
Framework-Specific Guidance
React with Vite
Only use VITE_ prefix for truly public values. All secrets go in server-side functions.
Next.js
Use server components or API routes for secret keys. Only NEXT_PUBLIC_ variables reach the client.
Create React App
REACT_APP_ variables are bundled into the client. Use a backend proxy for any calls requiring secret keys.
Angular
Environment files (environment.ts) are compiled into the browser bundle. Treat them as public.
The Bottom Line
Frontend API key exposure is one of the most common and most preventable security vulnerabilities in web applications. The fix is straightforward: keep secrets on the server, use environment variables correctly, and scan your code regularly.
If you are building with an AI code generator, this risk is even higher because AI tools often take the shortest path to a working integration, which means putting the key directly in the frontend. Always review generated code for exposed keys before deploying.
Run a scan on your application today. It takes minutes, and it might save you from a costly breach.
Real-World Key Exposure Incidents
These examples illustrate why frontend API key security matters beyond theory.
The GitHub Scanning Program
GitHub scans every public commit for known API key patterns and automatically notifies the affected service provider. In 2023, GitHub reported finding over 10 million exposed secrets in public repositories. Many of these came from developers who committed .env files or hardcoded keys during development and forgot to remove them.
Cloud Provider Auto-Exploitation
Attackers run automated scripts that detect newly exposed AWS keys within minutes. Once detected, they spin up high-cost compute instances for cryptocurrency mining. Victims have reported charges exceeding $50,000 from a single exposed key that was live for less than 24 hours. The speed of exploitation means that even briefly exposed keys can cause serious damage.
The Takeaway for Frontend Developers
These incidents happen constantly. The combination of AI code generators, which often hardcode keys for convenience, and public repositories creates a perfect storm. The fix is simple but requires discipline: never put secret keys in frontend code, always use server-side proxies, and scan your code regularly.