You’re building something fast. You open Cursor, type “connect to the OpenAI API and summarize this text,” and within seconds you have working code. You run it. It works. You copy it into your project and move on.
Except buried in that code is this:
openai.api_key = "sk-proj-AbC123xYz..."
That key is now in your source file. And if you push that file to GitHub (even a private repo, even for one minute) it may already be too late.
This is how API key leaks actually happen in 2025. Not through sophisticated attacks. Not through compromised infrastructure. Through AI-generated code that treats your secrets like any other variable.
Why AI tools keep doing this
The pattern isn’t random. There’s a specific reason AI coding assistants reach for hardcoded keys.
These models trained on billions of lines of public code. A huge chunk of that code has secrets baked right in: tutorials, demo projects, Stack Overflow answers, blog posts showing “quick start” examples. The pattern api_key = "sk-..." appears constantly in training data, usually paired with working, well-upvoted code. The model learned that this is how you wire up an API.
Most AI assistants also lack persistent context about your project structure. They don’t know you have a .env file. They don’t know you’re using Vault or AWS Secrets Manager. Every new prompt is a fresh start, and the fastest path to a working answer involves the pattern that appears most often in training data: inline the key.
There’s also something more subtle going on. When you ask for “a quick integration,” you’re signaling urgency. The model optimizes for getting you to working code fast. Pulling from an environment variable requires explaining the setup, which creates friction. Hardcoding gets you there in one step.
None of this is intentional. The models aren’t trying to compromise your security. They’re just predicting the most likely next token, and the most likely next token after openai.api_key = in their training data was a string literal.
The failure patterns showing up in the wild
Hardcoded keys in source files are the obvious one, but they’re not the only way AI-generated code bleeds secrets.
Logging to stdout. AI assistants often add debug output that includes request headers or full API calls. Something like print(f"Making request with key: {api_key}") shows up in generated code more than you’d expect. Your logs go somewhere: cloud log aggregators, Datadog, Splunk. Keys in logs are keys at rest in systems you don’t fully control.
Keys in comments. Some generated code includes example values in comments, like # Replace with your key: sk-proj-.... If the developer pastes their real key into the comment for “testing” and never removes it, it ships.
Config files without .gitignore entries. An AI might correctly put your key in a config.json or settings.py file, but not add that file to .gitignore. The key isn’t hardcoded in the main source, but it’s one git add . away from being public.
Environment variable names that never get set. This one doesn’t leak directly, but it creates a different problem. The AI writes os.environ.get("MY_API_KEY"), you deploy without actually setting the variable, and now your app is making unauthenticated requests or throwing errors that expose internal structure. In a panic to fix it, developers sometimes hardcode the key “temporarily.”
What happens when it goes wrong
In March 2023, Samsung engineers pasted proprietary source code into ChatGPT to help with debugging. The content became part of OpenAI’s training pipeline. Samsung’s code, including internal tooling and meeting notes, was now outside company control. Samsung banned ChatGPT after the incident, but the data was already gone.
That’s a different vector than hardcoded keys, but the failure mode is identical: a developer trusting an AI tool with sensitive data and not fully understanding where that data goes.
On the secrets-in-code side, the numbers are bad. GitGuardian detected over 12.8 million secrets exposed in public GitHub repositories in 2023 alone. GitHub’s own secret scanning program flagged more than 1 million leaked secrets in public repos in a single year. These aren’t ancient repositories nobody maintains. A significant portion are active projects.
The typical breach timeline: key gets committed, GitHub’s secret scanning picks it up (if you have it enabled), you get an alert, you rotate the key. Best case, nothing happened in that window. Worst case, automated scrapers that watch GitHub 24/7 grabbed the key within seconds of the push and it’s already been used.
AWS, Stripe, Twilio, and most major API providers have automated processes that watch public repos for their key patterns. But that only helps if you’re using those providers’ official tooling. For every other API, you’re on your own.
What you should actually do
Start with the basics. No secret belongs in source code. Not even temporarily. Not even in a “private” repo.
Use a .env file at the project root, add .env to your .gitignore before your first commit, and load it with python-dotenv, dotenv for Node, or the equivalent for your stack. Five-minute setup, eliminates the most common failure mode.
# .env
OPENAI_API_KEY=sk-proj-...
STRIPE_SECRET_KEY=sk_live_...
# .gitignore
.env
.env.local
.env.*.local
For production, skip .env files entirely. Use proper secret management: AWS Secrets Manager, HashiCorp Vault, Doppler, or your platform’s native secrets injection. The key should never touch disk in your production environment.
Run git-secrets or gitleaks as a pre-commit hook. These tools scan your commits before they leave your machine and block anything that looks like a credential. A five-second pre-commit check catches more leaks than most security audits.
When using an AI coding assistant, get into the habit of asking explicitly: “Use an environment variable for the API key and show me how to set it up.” The model will do it. It just needs the nudge. You can also add a note to your project’s .cursorrules or Copilot instructions telling the AI never to hardcode credentials.
A layer that actually helps at runtime
Even when you do everything right with secrets management, your API keys are still exposed at the network level. Any service you’re calling directly from your app can see your full key on every request. If that key gets scraped from logs, intercepted, or leaked through a third-party dependency, you often don’t know until damage is done.
This is where API Stronghold fits in. Instead of your app calling api.openai.com directly with your key, you route through API Stronghold. Your actual key never leaves your secured backend. You issue short-lived, scoped tokens to your app, and Stronghold handles the authenticated call to the upstream provider.
If a token leaks, you revoke it. The upstream key stays clean. You get logs on every request so you know exactly what was called, when, and from where.
Think of it like the difference between handing your car keys to a valet versus handing them a parking ticket. Same outcome, much less exposure.
Check out how API Stronghold works if you want the longer explanation.
The actual takeaway
AI coding tools are genuinely useful. They’re not going away, and fighting that fact is a waste of energy. But they were trained on code that normalized bad security patterns, and they’ll keep suggesting those patterns unless you actively steer them away.
The good news: the fix isn’t complicated. A .env file, a pre-commit hook, and some awareness of what you’re copying from AI suggestions covers the vast majority of cases. It takes about 20 minutes to set up on a new project and becomes muscle memory fast.
Don’t wait for a breach to care about this. Rotating a leaked key is stressful, time-consuming, and occasionally very expensive depending on what ran against it. The prevention side is genuinely easy.