On May 20, 2026, GitHub confirmed that attackers stole data from roughly 3,800 internal code repositories. The entry point was not a GitHub server zero-day. It was a poisoned VS Code extension installed on one employee laptop. Once the extension ran with the developer’s file-system permissions, it could read source code, config files, and credentials in the workspace. If you want to protect API keys from the same class of attack, focus on the developer machine and the tools running on it.
TL;DR
To protect API keys from compromised IDE extensions and leaked repositories:
- Do not hardcode live credentials.
- Do not commit real
.envfiles. - Treat
.gitignoreas hygiene, not a security boundary. - Scope each key to one environment.
- Prefer least-privilege, short-lived credentials.
- Rotate keys on a schedule.
- Keep production secrets off developer laptops whenever possible.
- Use tools like Apidog to reference credentials through environment variables with local-only values instead of scattering plaintext secrets across your repo.
Why the GitHub breach matters for developers
GitHub’s incident looks like a supply-chain attack against the developer workstation. The threat group, tracked as TeamPCP, has previously trojanized packages across npm, PyPI, and PHP ecosystems. This time, the malicious payload arrived through a VS Code extension.
According to TechCrunch’s reporting, attackers exfiltrated data from about 3,800 internal repositories and attempted to sell the dataset on underground forums. GitHub said it had no evidence that customer data stored outside those internal repos was affected, and the investigation was ongoing.
The key lesson: a VS Code extension is code running with your user permissions.
That means it can usually:
- List files in your workspace
- Open and read files
- Watch file changes
- Read config files
- Make outbound network requests
That is not necessarily a vulnerability. Many extensions need this access to work. The problem is that a malicious or compromised extension can use the same access to scrape secrets.
In a typical developer environment, that may include:
.envconfig/secrets.yml- Hardcoded test tokens
~/.aws/credentials-
.npmrcauth tokens - SSH keys
- API client collections
- Local scripts with temporary credentials
This exposure pattern is similar to the one covered in our breakdown of API security lessons from the Vercel breach and our npm supply chain security guide.
The practical question is simple:
If a malicious extension ran in your editor right now, what secrets could it read?
Hardcoded keys and committed .env files are long-term liabilities
Most credential leaks are not sophisticated. They usually happen because someone:
- Pasted a key into code “temporarily”
- Committed a real
.envfile - Stored production credentials in a local script
- Reused the same key across development, staging, and production
Bad pattern: hardcoded API key
import requests
# Quick test of the payments endpoint
STRIPE_KEY = "sk_live_51Qk2mNExampleKeyDoNotShipThis"
response = requests.post(
"https://api.stripe.com/v1/charges",
auth=(STRIPE_KEY, ""),
data={"amount": 2000, "currency": "usd", "source": "tok_visa"},
)
print(response.json())
This key is now:
- In the file system
- Readable by local tools
- Easy to commit accidentally
- Stored in Git history if committed
- Available to anyone or anything that can read the repo
Deleting the line later does not remove it from history.
Better, but still risky: plaintext .env
# .env (loaded at runtime, never meant to ship)
DATABASE_URL=postgres://app_user:Zk7%2BqN9wLx@db.internal:5432/payments
STRIPE_SECRET_KEY=sk_live_51Qk2mNExampleKeyDoNotShipThis
OPENAI_API_KEY=sk-proj-aB3dEf9hKlMnOpQrStUvWxYz1234567890
AWS_ACCESS_KEY_ID=AKIA4EXAMPLE7QRSTUVW
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
JWT_SIGNING_SECRET=8f2a91c4e7b6d3508f2a91c4e7b6d350
A .env file is better than hardcoding secrets into source code, but it still has two major weaknesses:
- It is plaintext on disk.
- It is readable by any local process with access to your workspace.
A malicious extension does not care whether the secret is in app.py or .env. Both are files. Both can be read.
The worst case is a committed .env. Once committed, real credentials may live indefinitely in repository history, mirrors, forks, backups, and leaked datasets.
For more on repository exposure, see our guide on API documentation and Git repository security.
.gitignore is not a security control
Adding .env to .gitignore is useful, but it is not protection.
.gitignore only tells Git to ignore untracked files that match a pattern when adding files. It does not encrypt secrets, remove secrets from history, or stop local tools from reading files.
Failure mode 1: already-tracked files stay tracked
If .env was committed before you added the ignore rule, Git keeps tracking it.
You must remove it from the index:
git rm --cached .env
git commit -m "Remove .env from tracking"
But that does not remove the secret from previous commits.
Failure mode 2: the file still exists on disk
Even if .env is ignored, the file still sits in your workspace:
.env
A malicious VS Code extension reads the file system, not your Git ignore rules.
Failure mode 3: ignores can be bypassed
A developer or tool can still force-add the file:
git add -f .env
An editor’s source-control UI or a nested .gitignore setup can also create mistakes.
Check whether .env was ever committed
Run:
# List every commit that touched .env
git log --all --full-history --oneline -- .env
# Search historical diffs for likely secrets
git log -p --all -- .env | grep -iE "key|secret|token|password"
If this returns real credentials, assume they are compromised.
Do this next:
- Rotate the exposed credential.
- Remove it from Git history with a tool like
git filter-repo. - Coordinate with your team before force-pushing cleaned history.
- Move the live credential out of plaintext files.
Use .gitignore for hygiene. Do not treat it as a security boundary.
Four habits that reduce API key blast radius
You cannot guarantee that a credential will never leak. You can make sure a leaked credential is less useful.
1. Scope secrets to environments
Never use one key for development, staging, and production.
Use separate credentials:
Development -> sandbox key
Staging -> staging key
Production -> production key
If a development key leaks, the attacker should only reach development resources with fake or non-sensitive data.
Production credentials should not be copied to laptops unless there is a very specific, approved need.
2. Separate environments properly
Environment separation is not just different variable names.
A safe setup means:
- Development uses a separate database.
- Staging uses test-mode payment providers.
- Production data is not reachable from local configs.
- A “dev” config cannot point to production by changing one variable.
- Logs, queues, storage buckets, and third-party integrations are environment-specific.
The goal is to make the answer to this question obvious:
If this key leaks, which environment is affected?
3. Use least privilege and short-lived credentials
A key should have the minimum permission required for its job.
For example:
Frontend catalog build:
allowed: read product catalog
denied: write products, access billing, manage users
Avoid admin tokens for automation unless there is no alternative.
Also prefer short-lived tokens where possible. A token that expires in one hour is far less valuable than a static key that works forever.
If you are comparing credential patterns, our guide to API keys versus OAuth explains when short-lived OAuth tokens are a better fit than static API keys.
4. Rotate keys on a schedule
Do not wait for an incident to learn how rotation works.
Set a rotation interval by risk level:
High-privilege production keys -> monthly
Lower-risk keys -> quarterly
Temporary development keys -> short expiry where possible
Scheduled rotation helps because:
- Undetected leaks expire sooner.
- Your team practices the process regularly.
- Incident response becomes routine instead of improvised.
For more options, see our roundup of API key management tools.
Keep API credentials out of loose workspace files with Apidog
Here is the practical framing: Apidog ships a VS Code extension and an MCP server. The point is not that any client-side tool is immune to supply-chain attacks. No local tool is.
The point is where your secrets live.
If you are building and testing APIs, you often need:
- Bearer tokens
- API keys
- Database connection strings
- Service credentials
- Environment-specific base URLs
The default move is to put those values in .env or local scripts. That creates plaintext secrets in the exact workspace that extensions can inspect.
Apidog’s environment system helps reduce that exposure.
Use environment variables instead of plaintext request values
In Apidog, store credentials as environment variables, then reference them in requests.
Instead of this:
Authorization: Bearer sk-proj-aB3dEf9hKlMnOpQrStUvWxYz1234567890
Use this:
Authorization: Bearer {{access_token}}
Your request definition stores the variable reference, not the literal secret.
That means the credential is not sitting as a plaintext line in a repo-level .env file or hardcoded request file.
Store sensitive values as local-only values
Apidog separates variable values into shared and local values.
Use shared values for non-sensitive defaults, such as:
base_url=https://api.example.test
Use local values for secrets, such as:
access_token
db_password
payment_api_key
The local value stays on your machine and is not uploaded as shared project data. Teammates can see the variable name and expected structure, but not your actual token.
This supports a safer workflow:
- Define the variable name in the project.
- Each developer adds their own local value.
- Real secrets do not travel in synced project configuration.
- Production tokens do not need to be shared through files or chat.
Isolate credentials by environment
Apidog’s environment management lets you define separate environments, such as:
Development
Staging
Production
Each environment can have its own:
- Base URL
- API token
- Account ID
- Database value
- Service-specific credentials
Example:
Development:
base_url=https://api-dev.example.com
payment_api_key={{local sandbox key}}
Staging:
base_url=https://api-staging.example.com
payment_api_key={{local staging key}}
Production:
base_url=https://api.example.com
payment_api_key={{production key, restricted access}}
You switch environments instead of editing requests.
That reduces mistakes like:
- Sending a dev request to production
- Reusing one key everywhere
- Copying production secrets into local files
- Testing destructive actions against live systems
Use vault-backed secrets when production values need a hard boundary
For teams that need production secrets to stay outside the API client, Apidog’s Enterprise plan includes a Vault Secret feature.
It can fetch secrets from:
- HashiCorp Vault
- Azure Key Vault
- AWS Secrets Manager
Apidog stores the vault path and metadata. The actual secret remains in the dedicated secrets manager and is pulled on demand.
That is the preferred model for production credentials: the secret’s home remains the secrets manager, not a developer laptop or project file.
A practical API key cleanup checklist
Use this checklist on the repo you work in most.
1. Search for obvious secrets
grep -RniE "api[_-]?key|secret|token|password|credential" .
Also check common files:
find . -name ".env*" -o -name "*.pem" -o -name "*.key" -o -name ".npmrc"
2. Check Git history
git log --all --full-history --oneline -- .env
git log -p --all | grep -iE "api[_-]?key|secret|token|password"
3. Rotate anything suspicious
If a credential appears in source, .env, logs, or Git history, rotate it.
Do not spend time debating whether it was accessed. Treat it as exposed.
4. Remove secrets from tracked files
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Stop tracking local environment file"
Then clean history if needed with git filter-repo.
5. Replace plaintext values with variables
For API testing, move from literal values to variable references:
Authorization: Bearer {{access_token}}
Then store the actual value as a local-only environment variable in your API client.
You can Download Apidog, create a project, open Environment Management, and add credentials as environment variables with local-only values.
Important caveat
Moving secrets into Apidog reduces how many plaintext credentials sit in your repo and workspace. It does not make your machine immune to compromised tools.
You still need defense in depth:
- Audit extensions before installing them.
- Remove extensions you do not use.
- Keep IDEs and extensions updated.
- Use least-privilege credentials.
- Prefer short-lived tokens.
- Keep production keys off laptops.
- Rotate keys on schedule.
- Use a dedicated secrets manager for production.
Apidog helps with the “where do API credentials live?” problem. It does not replace secure workstation and supply-chain practices.
Conclusion
The GitHub breach is a reminder that developer machines are high-value targets. Attackers do not always need to break into production servers. Sometimes they only need to compromise a trusted local tool that can read your workspace.
Start with one repo today:
- Search for
key,secret,token, andpassword. - Check whether
.envwas ever committed. - Rotate anything exposed.
- Remove secrets from history where needed.
- Move live API credentials out of plaintext files.
- Use environment-scoped, least-privilege credentials.
If you want a safer API testing workflow, Download Apidog and store your next set of API credentials as environment variables instead of plaintext workspace files.
For more context, read our companion articles on self-hosted API tools after the GitHub breach and API key management tools.
FAQ
Can a VS Code extension really read my .env file and API keys?
Yes. A VS Code extension runs with your user account’s file permissions inside the editor process. It can list directories, open files, and read contents, including .env, config files, and credential files like ~/.aws/credentials.
That access is normal for many extensions. The risk is that a malicious or compromised extension uses it to harvest secrets.
Is adding .env to .gitignore enough to protect API keys?
No. .gitignore only prevents untracked matching files from being added by default.
It does not:
- Protect files already committed
- Remove secrets from Git history
- Encrypt local files
- Stop extensions or local tools from reading
.env - Prevent forced adds with
git add -f
Use .gitignore, but do not rely on it as a security control.
What should I do if I find an API key in Git history?
Treat it as compromised.
Do this:
- Rotate the key immediately.
- Remove the secret from history with a tool like
git filter-repo. - Coordinate with your team before rewriting shared history.
- Move the live credential out of plaintext files.
- Add scanning and review steps to prevent repeats.
For ongoing practices, see our API key management tools guide.
How does storing API keys in Apidog reduce exposure?
Apidog lets you store credentials as environment variables and reference them by name in requests.
For example:
Authorization: Bearer {{access_token}}
The literal secret does not need to sit in a plaintext .env file in your repo. Sensitive values can be stored as local-only values, and environment-scoped variables keep development, staging, and production credentials separate.
This reduces the number of live secrets exposed in files a local tool can scrape. It does not make your machine immune to compromise.
Does Apidog also have a VS Code extension, and is that a risk?
Yes. Apidog ships a VS Code extension and an MCP server.
The point is not that any client-side tool is immune to supply-chain attacks. The point is to reduce the amount of plaintext secret material stored in your repo and workspace.
You should still audit extensions, use least privilege, avoid production keys on laptops, and rotate credentials regularly.
What is the difference between scoping and rotating API keys?
Scoping limits what a key can access.
Example:
dev key -> sandbox only
read-only key -> cannot write
billing key -> billing APIs only
Rotation changes the key value so the old one stops working.
You need both:
- Scoping reduces impact.
- Rotation reduces the useful lifetime of a leak.
How often should I rotate API keys?
Use a fixed schedule.
A reasonable baseline:
High-privilege production keys -> monthly
Lower-risk keys -> quarterly
Temporary credentials -> shortest practical expiry
Adjust based on risk, compliance requirements, and operational constraints.
Should production API keys ever be on a developer laptop?
Ideally, no.
Production credentials should exist in as few places as possible, usually:
- A dedicated secrets manager
- The production runtime
- Controlled CI/CD systems when required
Developers should use development or staging credentials scoped to non-production data. If a laptop is compromised, the attacker should reach a sandbox, not live customer systems.
Top comments (0)