[{"data":1,"prerenderedAt":567},["ShallowReactive",2],{"blog-vulnerabilities/exposed-api-keys":3},{"id":4,"title":5,"body":6,"category":543,"date":544,"dateModified":544,"description":545,"draft":546,"extension":547,"faq":548,"featured":546,"headerVariant":552,"image":553,"keywords":553,"meta":554,"navigation":555,"ogDescription":556,"ogTitle":553,"path":557,"readTime":558,"schemaOrg":559,"schemaType":560,"seo":561,"sitemap":562,"stem":563,"tags":564,"twitterCard":565,"__hash__":566},"blog/blog/vulnerabilities/exposed-api-keys.md","Exposed API Keys Explained: The #1 Vibe Coding Vulnerability",{"type":7,"value":8,"toc":518},"minimark",[9,16,22,27,30,33,68,72,75,90,93,102,106,111,114,128,132,215,219,223,232,236,239,248,252,261,265,274,278,282,291,295,304,308,317,321,324,339,348,352,413,417,420,450,459,487,506],[10,11,12],"tldr",{},[13,14,15],"p",{},"Exposed API keys happen when secret credentials end up in public places like GitHub repos, frontend JavaScript, or browser network requests. Attackers find these keys within minutes using automated bots. The fix is simple: move keys to environment variables and never commit them to version control. If a key is already exposed, rotate it immediately.",[17,18,19],"stat-callout",{},[13,20,21],{},"$50,000+\nAverage cost when AWS keys are exposed and abused for crypto mining\nSource: GitGuardian State of Secrets Sprawl 2024",[23,24,26],"h2",{"id":25},"what-are-exposed-api-keys","What Are Exposed API Keys?",[13,28,29],{},"An API key is like a password that lets your application access external services. When you use Stripe for payments, OpenAI for AI features, or AWS for cloud services, you get API keys to authenticate your requests.",[13,31,32],{},"Exposed API keys occur when these credentials become visible to people who shouldn't have them. This happens in several ways:",[34,35,36,44,50,56,62],"ul",{},[37,38,39,43],"li",{},[40,41,42],"strong",{},"Committed to Git:"," Keys pushed to public (or even private) repositories",[37,45,46,49],{},[40,47,48],{},"Hardcoded in frontend:"," Keys visible in JavaScript that runs in the browser",[37,51,52,55],{},[40,53,54],{},"Visible in network requests:"," Keys sent in URLs or headers that anyone can see",[37,57,58,61],{},[40,59,60],{},"Logged to consoles:"," Keys printed in error messages or debug output",[37,63,64,67],{},[40,65,66],{},"Included in build artifacts:"," Keys bundled into compiled code",[23,69,71],{"id":70},"why-this-is-the-1-vibe-coding-issue","Why This Is the #1 Vibe Coding Issue",[13,73,74],{},"AI coding assistants are trained on public code, which often includes examples with placeholder API keys. When you ask an AI to integrate Stripe or OpenAI, it generates code with hardcoded values:",[76,77,79],"code-block",{"label":78},"What AI might generate",[80,81,86],"pre",{"className":82,"code":84,"language":85},[83],"language-text","// This is what Cursor, Bolt, or Copilot might produce\nconst stripe = require('stripe')('sk_live_abc123xyz789');\nconst openai = new OpenAI({ apiKey: 'sk-proj-abcdef123456' });\n","text",[87,88,84],"code",{"__ignoreMap":89},"",[13,91,92],{},"If you ship this code to production or commit it to GitHub, your keys are exposed. Bots scan for these patterns constantly and will find them within minutes.",[94,95,96],"danger-box",{},[13,97,98,101],{},[40,99,100],{},"Real impact:"," GitGuardian detected over 12.8 million new secrets exposed in public GitHub repositories in 2023 alone. Many belong to vibe coders who didn't realize their AI-generated code contained real credentials.",[23,103,105],{"id":104},"how-attackers-find-exposed-keys","How Attackers Find Exposed Keys",[107,108,110],"h3",{"id":109},"automated-scanning","Automated Scanning",[13,112,113],{},"Attackers run bots that continuously:",[34,115,116,119,122,125],{},[37,117,118],{},"Monitor GitHub's public event stream for new commits",[37,120,121],{},"Search for patterns matching known API key formats",[37,123,124],{},"Scan websites for keys in JavaScript files",[37,126,127],{},"Check browser developer tools and network requests",[107,129,131],{"id":130},"key-patterns-they-look-for","Key Patterns They Look For",[133,134,135,151],"table",{},[136,137,138],"thead",{},[139,140,141,145,148],"tr",{},[142,143,144],"th",{},"Service",[142,146,147],{},"Key Pattern",[142,149,150],{},"What Attackers Do",[152,153,154,166,182,193,204],"tbody",{},[139,155,156,160,163],{},[157,158,159],"td",{},"AWS",[157,161,162],{},"AKIA... (20 chars)",[157,164,165],{},"Spin up crypto miners, access S3 buckets",[139,167,168,171,179],{},[157,169,170],{},"Stripe",[157,172,173,174,178],{},"sk",[175,176,177],"em",{},"live","...",[157,180,181],{},"Issue refunds, access customer data",[139,183,184,187,190],{},[157,185,186],{},"OpenAI",[157,188,189],{},"sk-...",[157,191,192],{},"Run up massive API bills",[139,194,195,198,201],{},[157,196,197],{},"Twilio",[157,199,200],{},"SK... (32 chars)",[157,202,203],{},"Send spam SMS, phishing calls",[139,205,206,209,212],{},[157,207,208],{},"SendGrid",[157,210,211],{},"SG....",[157,213,214],{},"Send spam and phishing emails",[23,216,218],{"id":217},"common-places-keys-get-exposed","Common Places Keys Get Exposed",[107,220,222],{"id":221},"_1-frontend-javascript","1. Frontend JavaScript",[76,224,226],{"label":225},"Exposed in browser (anyone can see this)",[80,227,230],{"className":228,"code":229,"language":85},[83],"// In your React component\nconst response = await fetch('https://api.openai.com/v1/chat', {\n  headers: {\n    'Authorization': 'Bearer sk-proj-your-secret-key' // Visible to everyone!\n  }\n});\n",[87,231,229],{"__ignoreMap":89},[107,233,235],{"id":234},"_2-git-history","2. Git History",[13,237,238],{},"Even if you remove a key from your current code, it still exists in your Git history:",[76,240,242],{"label":241},"Keys live forever in Git history",[80,243,246],{"className":244,"code":245,"language":85},[83],"# This command shows all previous versions of a file\ngit log -p --all -S 'sk_live' -- .\n\n# Attackers can find keys you \"removed\" months ago\n",[87,247,245],{"__ignoreMap":89},[107,249,251],{"id":250},"_3-environment-files-committed","3. Environment Files Committed",[76,253,255],{"label":254},".env file that got committed by accident",[80,256,259],{"className":257,"code":258,"language":85},[83],"# This should NEVER be in your repo\nSTRIPE_SECRET_KEY=sk_live_abc123\nDATABASE_URL=postgres://user:password@host/db\nOPENAI_API_KEY=sk-proj-xyz789\n",[87,260,258],{"__ignoreMap":89},[107,262,264],{"id":263},"_4-build-artifacts-and-logs","4. Build Artifacts and Logs",[76,266,268],{"label":267},"Keys leaking into logs",[80,269,272],{"className":270,"code":271,"language":85},[83],"// Debug logging that exposes keys\nconsole.log('Config:', config); // Prints all keys to console\nconsole.log('Request headers:', headers); // Includes auth tokens\n",[87,273,271],{"__ignoreMap":89},[23,275,277],{"id":276},"how-to-fix-exposed-api-keys","How to Fix Exposed API Keys",[107,279,281],{"id":280},"step-1-use-environment-variables","Step 1: Use Environment Variables",[76,283,285],{"label":284},"Correct approach",[80,286,289],{"className":287,"code":288,"language":85},[83],"// Server-side code\nconst stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);\nconst openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n\n// Never hardcode keys directly!\n",[87,290,288],{"__ignoreMap":89},[107,292,294],{"id":293},"step-2-configure-gitignore","Step 2: Configure .gitignore",[76,296,298],{"label":297},".gitignore",[80,299,302],{"className":300,"code":301,"language":85},[83],"# Environment files\n.env\n.env.local\n.env.*.local\n.env.production\n\n# IDE and system files\n.idea/\n.vscode/\n.DS_Store\n",[87,303,301],{"__ignoreMap":89},[107,305,307],{"id":306},"step-3-use-backend-for-sensitive-api-calls","Step 3: Use Backend for Sensitive API Calls",[76,309,311],{"label":310},"Keep secrets on the server",[80,312,315],{"className":313,"code":314,"language":85},[83],"// Frontend calls YOUR backend\nconst response = await fetch('/api/generate-text', {\n  method: 'POST',\n  body: JSON.stringify({ prompt: userInput })\n});\n\n// Backend (server-side) calls OpenAI with the secret key\n// app/api/generate-text/route.ts\nexport async function POST(req) {\n  const { prompt } = await req.json();\n  const response = await openai.chat.completions.create({\n    model: 'gpt-4',\n    messages: [{ role: 'user', content: prompt }]\n  });\n  return Response.json(response);\n}\n",[87,316,314],{"__ignoreMap":89},[107,318,320],{"id":319},"step-4-rotate-exposed-keys-immediately","Step 4: Rotate Exposed Keys Immediately",[13,322,323],{},"If a key has been exposed, you cannot \"unexpose\" it. You must:",[325,326,327,330,333,336],"ol",{},[37,328,329],{},"Generate a new key in your service provider's dashboard",[37,331,332],{},"Update your application to use the new key",[37,334,335],{},"Delete or deactivate the old key",[37,337,338],{},"Check for unauthorized usage in your billing/logs",[340,341,342],"success-box",{},[13,343,344,347],{},[40,345,346],{},"Pro tip:"," Many services (GitHub, GitLab, AWS) offer secret scanning that automatically detects and alerts you when keys are committed. Enable these features.",[23,349,351],{"id":350},"platform-specific-guidance","Platform-Specific Guidance",[133,353,354,367],{},[136,355,356],{},[139,357,358,361,364],{},[142,359,360],{},"Platform",[142,362,363],{},"Where to Store Keys",[142,365,366],{},"Documentation",[152,368,369,380,391,402],{},[139,370,371,374,377],{},[157,372,373],{},"Vercel",[157,375,376],{},"Project Settings > Environment Variables",[157,378,379],{},"vercel.com/docs/environment-variables",[139,381,382,385,388],{},[157,383,384],{},"Netlify",[157,386,387],{},"Site Settings > Environment Variables",[157,389,390],{},"docs.netlify.com/environment-variables",[139,392,393,396,399],{},[157,394,395],{},"Railway",[157,397,398],{},"Project > Variables",[157,400,401],{},"docs.railway.app/develop/variables",[139,403,404,407,410],{},[157,405,406],{},"Replit",[157,408,409],{},"Secrets tab (lock icon)",[157,411,412],{},"docs.replit.com/programming-ide/secrets",[23,414,416],{"id":415},"what-keys-are-safe-in-frontend","What Keys Are Safe in Frontend?",[13,418,419],{},"Some keys are designed to be public. These are safe to include in frontend code:",[34,421,422,432,438,444],{},[37,423,424,427,428,431],{},[40,425,426],{},"Stripe Publishable Key"," (",[87,429,430],{},"pk_live_...",") - Designed for browser use",[37,433,434,437],{},[40,435,436],{},"Firebase Config"," - Public by design, secured by rules",[37,439,440,443],{},[40,441,442],{},"Supabase Anon Key"," - Public, protected by Row Level Security",[37,445,446,449],{},[40,447,448],{},"Google Maps API Key"," - Public, but restrict by domain",[451,452,453],"warning-box",{},[13,454,455,458],{},[40,456,457],{},"Important:"," Even \"public\" keys should be restricted. Configure domain restrictions, API quotas, and usage alerts to prevent abuse.",[460,461,462,469,475,481],"faq-section",{},[463,464,466],"faq-item",{"question":465},"What happens if my API key is exposed?",[13,467,468],{},"Attackers can use your API key to make requests on your behalf. This typically results in unexpected charges (sometimes thousands of dollars), data theft, or abuse of services like sending spam through your email API.",[463,470,472],{"question":471},"How do attackers find exposed API keys?",[13,473,474],{},"Bots continuously scan GitHub, GitLab, and public websites for patterns that match API keys. They check browser developer tools, JavaScript source code, and public repositories. Once found, keys are often exploited within minutes.",[463,476,478],{"question":477},"Can I undo an exposed API key?",[13,479,480],{},"You cannot undo the exposure, but you can mitigate damage by immediately rotating (replacing) the key. Go to your service provider's dashboard, generate a new key, update your application to use the new key, then delete the old one.",[463,482,484],{"question":483},"How do I remove a key from Git history?",[13,485,486],{},"You can use tools like git-filter-repo or BFG Repo Cleaner to rewrite history and remove secrets. However, if the repo was ever public, assume the key is compromised and rotate it regardless.",[488,489,490,496,501],"related-articles",{},[491,492],"related-card",{"description":493,"href":494,"title":495},"How Cursor, Bolt, and Lovable handle secrets — and how to stop leaks","/blog/best-practices/ai-api-key-exposure","Why AI Code Generators Expose Your API Keys",[491,497],{"description":498,"href":499,"title":500},"Step-by-step guide","/blog/how-to/secure-api-keys","How to Secure API Keys",[491,502],{"description":503,"href":504,"title":505},"Similar vulnerability explained","/blog/vulnerabilities/hardcoded-credentials","Hardcoded Credentials",[507,508,511,515],"cta-box",{"href":509,"label":510},"/","Start Free Scan",[23,512,514],{"id":513},"check-your-app-for-exposed-keys","Check Your App for Exposed Keys",[13,516,517],{},"Our scanner finds API keys in your code, Git history, and deployed app.",{"title":89,"searchDepth":519,"depth":519,"links":520},2,[521,522,523,528,534,540,541,542],{"id":25,"depth":519,"text":26},{"id":70,"depth":519,"text":71},{"id":104,"depth":519,"text":105,"children":524},[525,527],{"id":109,"depth":526,"text":110},3,{"id":130,"depth":526,"text":131},{"id":217,"depth":519,"text":218,"children":529},[530,531,532,533],{"id":221,"depth":526,"text":222},{"id":234,"depth":526,"text":235},{"id":250,"depth":526,"text":251},{"id":263,"depth":526,"text":264},{"id":276,"depth":519,"text":277,"children":535},[536,537,538,539],{"id":280,"depth":526,"text":281},{"id":293,"depth":526,"text":294},{"id":306,"depth":526,"text":307},{"id":319,"depth":526,"text":320},{"id":350,"depth":519,"text":351},{"id":415,"depth":519,"text":416},{"id":513,"depth":519,"text":514},"vulnerabilities","2026-01-16","API key exposure is the most common security issue in AI-generated code. Learn what exposed API keys are, why they're dangerous, and how to fix them fast.",false,"md",[549,550,551],{"question":465,"answer":468},{"question":471,"answer":474},{"question":477,"answer":480},"red",null,{},true,"Learn what exposed API keys are, why they're dangerous, and how to fix them in your vibe-coded app.","/blog/vulnerabilities/exposed-api-keys","8 min read","[object Object]","TechArticle",{"title":5,"description":545},{"loc":557},"blog/vulnerabilities/exposed-api-keys",[],"summary_large_image","aqxcxZoY5hU0ZphNnoqkaaco_Lo2iN85_vwIB4RdAsI",1775843918547]