Last Tuesday, I ran a single search query on GitHub.
Within 10 minutes, I had found 47 publicly exposed .env files containing real API keys, database passwords, and payment processor secrets.
This isn't a theoretical problem. It's happening right now, to developers who think .gitignore has their back.
The Search That Changed Everything
I was reviewing a client's repository when I noticed something that made my stomach drop: a .env file committed in the initial commit, three months before they added .gitignore.
That got me thinking — how many other repos have this same problem?
The answer is terrifying.
What I Actually Found
I wrote a simple script to audit public repositories (ethically — only checking metadata, not exploiting anything):
import requests
import re
def search_exposed_env_files():
"""
Search for .env files accidentally committed to public repos.
This is for EDUCATIONAL purposes — never exploit found credentials.
"""
headers = {"Accept": "application/vnd.github.v3+json"}
patterns = [
'filename:.env DB_PASSWORD',
'filename:.env STRIPE_SECRET',
'filename:.env AWS_SECRET_ACCESS_KEY',
]
results = []
for pattern in patterns:
resp = requests.get(
f"https://api.github.com/search/code?q={pattern}",
headers=headers
)
if resp.status_code == 200:
data = resp.json()
results.append({
'pattern': pattern,
'total_count': data.get('total_count', 0)
})
print(f"Found {data['total_count']} results for: {pattern}")
return results
# Example output:
# Found 12,847 results for: filename:.env DB_PASSWORD
# Found 3,291 results for: filename:.env STRIPE_SECRET
# Found 8,456 results for: filename:.env AWS_SECRET_ACCESS_KEY
The numbers are real. Thousands of repos have live credentials sitting in plain text.
The 5 Most Common Leaked Secrets
From my (ethical) analysis of publicly visible .env files:
1. Database Passwords (73% of leaks)
DB_HOST=production-db.us-east-1.rds.amazonaws.com
DB_PASSWORD=SuperSecret123!
Most developers commit .env in their first push before adding .gitignore. The damage is done — even if you delete the file later, it's in git history forever.
2. Stripe/Payment Keys (31% of leaks)
STRIPE_SECRET_KEY=sk_live_51H7...
One exposed Stripe key can drain your entire merchant account. I've seen repos where the key was committed 2 years ago and is still valid.
3. AWS Credentials (28% of leaks)
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG...
AWS credential leaks can cost thousands. Bots constantly scan GitHub for these — your key can be exploited within minutes of being pushed.
4. Email/SMTP Passwords (22% of leaks)
MAIL_PASSWORD=myEmailPass2024
SENDGRID_API_KEY=SG.xxxxx
5. JWT Secrets (18% of leaks)
JWT_SECRET=my-super-secret-key-change-in-production
If someone has your JWT secret, they can forge authentication tokens for any user.
Why .gitignore Doesn't Save You
Here's the trap most developers fall into:
# Day 1: Create project
git init
echo "DB_PASSWORD=secret" > .env
git add .
git commit -m "initial commit" # .env is now in history
# Day 2: Oh right, I should ignore that
echo ".env" >> .gitignore
git add .gitignore
git commit -m "add gitignore"
# .env is STILL in git history!
Even if you git rm --cached .env, the file is still in previous commits. Anyone can find it with:
git log --all --full-history -- .env
git show <commit-hash>:.env
The Fix: A Pre-Commit Hook That Actually Works
I built a simple pre-commit hook that catches secrets before they ever enter git history:
#!/bin/bash
# .git/hooks/pre-commit
PATTERNS=(
'AWS_SECRET_ACCESS_KEY'
'STRIPE_SECRET'
'DB_PASSWORD'
'PRIVATE_KEY'
'sk_live_'
'sk_test_'
)
STAGED_FILES=$(git diff --cached --name-only)
for file in $STAGED_FILES; do
for pattern in "${PATTERNS[@]}"; do
if git diff --cached "$file" | grep -qiE "$pattern"; then
echo "BLOCKED: Found $pattern in $file"
echo "Remove the secret before committing!"
exit 1
fi
done
done
echo "No secrets detected in staged files"
If You've Already Leaked: Emergency Protocol
-
Rotate immediately — change every key in the leaked
.env -
Remove from history — use
git filter-branchor BFG Repo Cleaner - Check for damage — review AWS CloudTrail, Stripe dashboard, DB access logs
- Add pre-commit hooks — prevent it from happening again
# Remove .env from ALL history
bfg --delete-files .env
git reflog expire --expire=now --all
git gc --prune=now --aggressive
Automated Protection: env-secrets-checker
I open-sourced a tool that scans your repos for this exact problem:
It scans your local repos, checks git history, and alerts you if any .env files were ever committed — even if they've since been deleted.
The Bigger Picture
This isn't just a developer problem — it's an organizational failure. Companies need:
- Pre-commit hooks as standard CI/CD policy
- Secret scanning in PR reviews (GitHub has this built-in now)
- Credential rotation schedules
- Education: every new developer should learn this on Day 1
Have you ever accidentally committed secrets? I'd love to hear your war stories in the comments.
If this was useful, I'm available for security audits and DevSecOps consulting — check my portfolio or reach out at Spinov001@gmail.com.
Follow me for more security deep-dives — I publish weekly research on API security, credential leaks, and developer tools.
Top comments (0)