You added .env to .gitignore. You deleted the committed version. You think you're safe.
You're probably not.
Git remembers everything. That .env file you committed 6 months ago — with your database password, Stripe keys, and AWS credentials — is still in your git history. Anyone who clones your repo can find it in seconds.
How Bad Is This?
GitGuardian's 2024 State of Secrets report found:
- 12.8 million new secrets leaked on GitHub in one year
- 39% of scanned repos contained at least one secret
- 90% of leaked secrets remain valid for 5+ days after detection
Check Your Repo in 30 Seconds
Run this in your project directory:
# Check if .env was ever committed
git log --all --diff-filter=A --name-only -- '.env*' | head -20
If you see output — your secrets are in the history.
The Deeper Check
# Find ALL secret-like files ever committed
git log --all --diff-filter=A --name-only --pretty=format: -- \
'*.env' '*.env.*' '*.pem' '*.key' 'credentials*' 'secrets*' \
| sort -u | grep -v '^$'
Automated Scanner
I built a Python tool that does a comprehensive check:
import re, subprocess
from pathlib import Path
SECRET_PATTERNS = {
'AWS Key': r'AKIA[0-9A-Z]{16}',
'GitHub Token': r'(ghp|gho)_[A-Za-z0-9_]{36,}',
'Stripe Key': r'sk_live_[0-9a-zA-Z]{24,}',
'Slack Token': r'xox[bpors]-[0-9A-Za-z\-]{10,}',
'Generic Secret': r'(?i)(secret|password)\s*[=:]\s*[^\s]{8,}',
'Database URL': r'(?i)(postgres|mysql|mongodb)://[^:]+:[^@]+@',
}
def scan_env_file(filepath):
issues = []
with open(filepath) as f:
for i, line in enumerate(f, 1):
for name, pattern in SECRET_PATTERNS.items():
if re.search(pattern, line):
issues.append(f" Line {i}: {name} detected")
return issues
def check_gitignore():
gi = Path('.gitignore')
if not gi.exists():
return "No .gitignore found!"
return ".env in .gitignore" if '.env' in gi.read_text() else ".env NOT in .gitignore!"
# Run it
print(check_gitignore())
for env_file in Path('.').glob('.env*'):
issues = scan_env_file(env_file)
if issues:
print(f"\n{env_file}: {len(issues)} secrets found")
for issue in issues:
print(issue)
For the full version with git history scanning and CI/CD integration, check out env-secrets-checker on GitHub.
If You Find Secrets in Your History
Don't panic. But do act fast:
Step 1: Rotate ALL exposed credentials
Before cleaning the git history, rotate every key that was exposed. Assume they've been compromised.
Step 2: Use git-filter-repo (NOT filter-branch)
# Install
pip install git-filter-repo
# Remove .env from entire history
git filter-repo --invert-paths --path .env --force
# Force push (this rewrites history for everyone)
git push --force --all
Step 3: Prevent future leaks
Add to .gitignore:
.env
.env.*
!.env.example
Add a pre-commit hook:
#!/bin/sh
if git diff --cached --name-only | grep -q '\.env'; then
echo "Blocked: .env file in staged changes"
exit 1
fi
Best Practices
- Never put real secrets in .env — use a secrets manager (Vault, AWS SSM, Doppler)
- Always have .env.example with placeholder values checked in
- Use pre-commit hooks to catch .env before it's committed
- Run automated scans in CI/CD pipeline
- Rotate keys regularly — even without a breach
The Checklist
- [ ] Is
.envin.gitignore? - [ ] Does
.env.exampleexist with placeholders? - [ ] Is there a pre-commit hook blocking
.envcommits? - [ ] Have you checked git history for past leaks?
- [ ] Are all detected secrets rotated?
Run that git log command on your repos today. You might be surprised what you find.
For a comprehensive scan, try env-secrets-checker — checks .env files, git history, config files, and Docker files in one command.
Follow for more security and DevOps content.
Top comments (0)