Security Leak Prevention
Preventing API key leaks and setting up detection systems before they become incidents
Last updated: 8/15/2025
Prevention is better than cure when it comes to API key leaks. Here's how to set up systems that stop secrets from escaping in the first place.
Common Leak Sources
Git Commits (Most Common)
# Dangerous patterns that often get committed
echo "SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIs..." > .env
git add .env # NEVER DO THIS
# Safe patterns
echo ".env*" >> .gitignore
echo "*.env" >> .gitignore
git add .gitignore # This is safe
CI/CD Logs
# Bad: secrets in build logs
echo "Connecting with key: $SUPABASE_SERVICE_ROLE_KEY"
# Good: hide sensitive values
echo "Connecting with key: ${SUPABASE_SERVICE_ROLE_KEY:0:8}..."
Development Tools
// Bad: secrets in browser console
console.log('Config:', {
supabaseKey: process.env.SUPABASE_SERVICE_ROLE_KEY
});
// Good: mask or omit secrets
console.log('Config:', {
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL,
keyLength: process.env.SUPABASE_SERVICE_ROLE_KEY?.length
});
.gitignore Setup
Comprehensive .gitignore
# Environment variables
.env
.env.*
.env.local
.env.development
.env.staging
.env.production
*.env
# OS files
.DS_Store
Thumbs.db
# IDE files
.vscode/settings.json
.idea/
*.swp
*.swo
# Dependencies
node_modules/
.pnpm-debug.log*
# Build outputs
.next/
dist/
build/
# Runtime files
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
Environment File Template
Create .env.example (safe to commit):
# .env.example
# Copy this to .env.local and fill in real values
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_here
# Other services
STRIPE_SECRET_KEY=sk_test_...
SENDGRID_API_KEY=SG...
Automated Detection Tools
Gitleaks (Local Scanning)
Install and run locally:
# Install gitleaks
brew install gitleaks
# Scan current repository
gitleaks detect --verbose --redact
# Scan git history
gitleaks detect --verbose --redact --log-opts="--all"
# Pre-commit hook
gitleaks protect --verbose --redact --staged
Pre-commit Hooks
Setup with Husky:
# Install husky
npm install --save-dev husky
# Add pre-commit hook
npx husky add .husky/pre-commit "gitleaks protect --staged"
Or manual git hook in .git/hooks/pre-commit:
#!/bin/sh
# Prevent commits with secrets
if command -v gitleaks >/dev/null 2>&1; then
gitleaks protect --staged
if [ $? -ne 0 ]; then
echo "β Secrets detected in staged files"
exit 1
fi
fi
# Additional patterns check
if git diff --cached --name-only | xargs grep -l "sk_live_\|sk_test_\|eyJhbGciOiJIUzI1NiIs"; then
echo "Potential secrets found in staged files"
exit 1
fi
GitHub Secret Scanning
Enable in repository settings:
- Settings (Code Security) (Secret Scanning)
- Enable "Secret scanning"
- Enable "Push protection" (blocks pushes with secrets)
- Configure custom patterns for your specific secrets
Custom patterns for Supabase keys:
# Service role keys (high entropy JWT-like strings)
eyJ[A-Za-z0-9+/=]{100,}
# Supabase project URLs
https://[a-z]{20}\.supabase\.co
Development Workflow Security
Environment Loading
// utils/env-check.js
const validateEnvironment = () => {
const required = [
'NEXT_PUBLIC_SUPABASE_URL',
'NEXT_PUBLIC_SUPABASE_ANON_KEY'
];
const serverRequired = [
'SUPABASE_SERVICE_ROLE_KEY'
];
// Check public vars
const missing = required.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(`Missing public env vars: ${missing.join(', ')}`);
}
// Check server vars (only on server)
if (typeof window === 'undefined') {
const missingServer = serverRequired.filter(key => !process.env[key]);
if (missingServer.length > 0) {
console.warn(`Missing server env vars: ${missingServer.join(', ')}`);
}
}
// Warn about potential mistakes
required.forEach(key => {
const value = process.env[key];
if (value && value.includes('your_') || value.includes('example')) {
console.warn(`β οΈ ${key} looks like a placeholder value`);
}
});
};
// Call during app initialization
validateEnvironment();
Safe Configuration Loading
// config/supabase.js
const getConfig = () => {
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
// Validate format
if (!url?.startsWith('https://')) {
throw new Error('Invalid Supabase URL format');
}
if (!anonKey?.startsWith('eyJ')) {
throw new Error('Invalid anon key format');
}
// Warn about common mistakes
if (url.includes('your-project')) {
console.warn('Supabase URL looks like a placeholder');
}
return { url, anonKey };
};
export const config = getConfig();
Team Security Practices
Onboarding Checklist
## New Developer Security Setup
- Install gitleaks: `brew install gitleaks`
- Setup pre-commit hooks: `npx husky add .husky/pre-commit "gitleaks protect --staged"`
- Copy `.env.example` to `.env.local`
- Get environment variables from team lead (never via Slack/email)
- Verify `.env.local` is in `.gitignore`
- Test local setup without committing secrets
- Review security guidelines document
Secret Sharing Protocol
## How to Share Secrets Safely
### Never Use
- Slack messages
- Email
- Text files in shared drives
- Screenshots
- Voice calls (someone might record)
### Safe Methods
- Password managers (1Password, Bitwarden)
- Encrypted notes apps
- In-person handoff
- Secure secret sharing services (with expiration)
Regular Security Reviews
#!/bin/bash
# scripts/security-audit.sh
echo "π Running security audit..."
# Check for secrets in codebase
echo "Checking for potential secrets..."
gitleaks detect --verbose --redact
# Check gitignore patterns
echo "Validating .gitignore..."
if grep -q "\.env" .gitignore; then
echo ".env patterns found in .gitignore"
else
echo "Missing .env patterns in .gitignore"
fi
# Check for hardcoded URLs/keys
echo "Checking for hardcoded secrets..."
grep -r "supabase\.co" . --exclude-dir=node_modules --exclude-dir=.git | grep -v ".env.example"
grep -r "eyJ[A-Za-z0-9]" . --exclude-dir=node_modules --exclude-dir=.git | grep -v ".env.example"
echo "π Audit complete"
Recovery Planning
Incident Response Preparation
## Secret Leak Response Plan
### Immediate (0-15 minutes)
1. Rotate exposed keys in Supabase dashboard
2. Remove keys from CI/CD environment variables
3. Deploy with temporary/blank values if needed
### Short-term (15-60 minutes)
1. Update all environments with new keys
2. Remove secrets from git history
3. Force-push cleaned repository
4. Audit logs for suspicious activity
### Follow-up (1-24 hours)
1. Review how leak occurred
2. Update prevention measures
3. Document lessons learned
4. Train team on new procedures
Key Rotation Schedule
// utils/key-rotation-reminder.js
const checkKeyAge = () => {
const keyCreatedDate = process.env.SUPABASE_KEY_CREATED_DATE;
if (!keyCreatedDate) {
console.warn('β οΈ No key creation date set - consider rotating keys');
return;
}
const created = new Date(keyCreatedDate);
const now = new Date();
const ageInDays = (now - created) / (1000 * 60 * 60 * 24);
if (ageInDays > 90) {
console.warn(`π Supabase keys are ${Math.floor(ageInDays)} days old - consider rotation`);
}
};
// Run during app startup in development
if (process.env.NODE_ENV === 'development') {
checkKeyAge();
}
Monitoring and Alerts
Log Analysis
# Check CI logs for secrets (run periodically)
grep -i "supabase\|key\|secret\|token" build-logs.txt | grep -v "REDACTED"
GitHub Repository Settings
- Enable vulnerability alerts
- Enable dependency security updates
- Require signed commits (optional but recommended)
- Branch protection rules for main/production branches
- Require pull request reviews before merging
Prevention requires building security into your development workflow, not just reacting to leaks after they happen. Start with these tools and practices from day one of your project.
Related Topics
Continue building your security knowledge:
- Security Incident Response - Emergency procedures for leaks
- API Keys and Environment Setup - Safe handling of credentials
- Multi-tenant Security Patterns - Team and organisation data isolation
- Supabase Client Patterns - Common usage patterns