Initial commit
This commit is contained in:
@@ -0,0 +1,530 @@
|
||||
# Secret Remediation Guide
|
||||
|
||||
Comprehensive procedures for remediating exposed secrets detected by Gitleaks.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Immediate Response](#immediate-response)
|
||||
- [Remediation Workflow](#remediation-workflow)
|
||||
- [Git History Cleanup](#git-history-cleanup)
|
||||
- [Cloud Provider Specific](#cloud-provider-specific)
|
||||
- [Database Credentials](#database-credentials)
|
||||
- [API Keys and Tokens](#api-keys-and-tokens)
|
||||
- [Post-Remediation](#post-remediation)
|
||||
|
||||
## Immediate Response
|
||||
|
||||
When secrets are detected, follow this priority order:
|
||||
|
||||
### 1. Assess Exposure (0-15 minutes)
|
||||
|
||||
**Questions to answer immediately:**
|
||||
- Is the repository public or private?
|
||||
- Has the commit been pushed to remote?
|
||||
- How long has the secret been exposed?
|
||||
- What systems does this credential access?
|
||||
|
||||
**Actions:**
|
||||
```bash
|
||||
# Check if commit is pushed
|
||||
git log origin/main..HEAD # If output, not yet pushed
|
||||
|
||||
# Check repository visibility
|
||||
gh repo view --json visibility
|
||||
|
||||
# Check commit age
|
||||
git log -1 --format="%ar" <commit-sha>
|
||||
```
|
||||
|
||||
### 2. Rotate Credentials (0-30 minutes)
|
||||
|
||||
**CRITICAL**: Rotate the exposed credential immediately, regardless of exposure duration.
|
||||
|
||||
Priority order:
|
||||
1. **Production credentials** - Immediate rotation
|
||||
2. **Payment/financial systems** - Immediate rotation
|
||||
3. **Customer data access** - Immediate rotation
|
||||
4. **Development/test credentials** - Rotate within 24 hours
|
||||
|
||||
### 3. Review Access Logs (30-60 minutes)
|
||||
|
||||
Check for unauthorized access:
|
||||
- Cloud provider audit logs (CloudTrail, Cloud Audit Logs, Activity Log)
|
||||
- Application logs showing authentication attempts
|
||||
- Database connection logs
|
||||
- API usage logs
|
||||
|
||||
### 4. Remove from Code (0-24 hours)
|
||||
|
||||
Remove secret from current code and optionally from git history.
|
||||
|
||||
## Remediation Workflow
|
||||
|
||||
### Step 1: Rotate the Credential
|
||||
|
||||
**Before removing from code**, rotate the credential to prevent race conditions.
|
||||
|
||||
#### Cloud Providers
|
||||
|
||||
**AWS**:
|
||||
```bash
|
||||
# Deactivate compromised key
|
||||
aws iam update-access-key \
|
||||
--access-key-id AKIA... \
|
||||
--status Inactive \
|
||||
--user-name username
|
||||
|
||||
# Create new key
|
||||
aws iam create-access-key --user-name username
|
||||
|
||||
# Delete old key after updating applications
|
||||
aws iam delete-access-key \
|
||||
--access-key-id AKIA... \
|
||||
--user-name username
|
||||
```
|
||||
|
||||
**GCP**:
|
||||
```bash
|
||||
# Delete service account key
|
||||
gcloud iam service-accounts keys delete KEY_ID \
|
||||
--iam-account=SERVICE_ACCOUNT_EMAIL
|
||||
|
||||
# Create new key
|
||||
gcloud iam service-accounts keys create new-key.json \
|
||||
--iam-account=SERVICE_ACCOUNT_EMAIL
|
||||
```
|
||||
|
||||
**Azure**:
|
||||
```bash
|
||||
# Regenerate storage account key
|
||||
az storage account keys renew \
|
||||
--account-name ACCOUNT_NAME \
|
||||
--key primary
|
||||
|
||||
# List keys to verify
|
||||
az storage account keys list \
|
||||
--account-name ACCOUNT_NAME
|
||||
```
|
||||
|
||||
#### API Tokens
|
||||
|
||||
**GitHub**:
|
||||
1. Navigate to Settings > Developer settings > Personal access tokens
|
||||
2. Find the compromised token (check "Last used" column)
|
||||
3. Click "Delete"
|
||||
4. Generate new token with minimal required scopes
|
||||
|
||||
**Stripe**:
|
||||
1. Log into Stripe Dashboard
|
||||
2. Navigate to Developers > API keys
|
||||
3. Click "Roll" on the compromised key
|
||||
4. Update all applications with new key
|
||||
|
||||
**Generic API Key**:
|
||||
1. Access provider's console/dashboard
|
||||
2. Locate API key management
|
||||
3. Revoke/delete compromised key
|
||||
4. Generate new key
|
||||
5. Update applications
|
||||
6. Test connectivity
|
||||
|
||||
### Step 2: Remove from Current Code
|
||||
|
||||
Replace hardcoded secrets with environment variables or secret management:
|
||||
|
||||
**Before** (insecure):
|
||||
```python
|
||||
API_KEY = "sk_live_51ABC123..."
|
||||
db_password = "MyP@ssw0rd123"
|
||||
```
|
||||
|
||||
**After** (secure):
|
||||
```python
|
||||
import os
|
||||
|
||||
API_KEY = os.environ.get("STRIPE_API_KEY")
|
||||
if not API_KEY:
|
||||
raise ValueError("STRIPE_API_KEY environment variable not set")
|
||||
|
||||
db_password = os.environ.get("DB_PASSWORD")
|
||||
```
|
||||
|
||||
**Using secret management**:
|
||||
```python
|
||||
from azure.keyvault.secrets import SecretClient
|
||||
from azure.identity import DefaultAzureCredential
|
||||
|
||||
credential = DefaultAzureCredential()
|
||||
client = SecretClient(vault_url="https://myvault.vault.azure.net/", credential=credential)
|
||||
|
||||
db_password = client.get_secret("database-password").value
|
||||
```
|
||||
|
||||
### Step 3: Commit the Fix
|
||||
|
||||
```bash
|
||||
# Add changes
|
||||
git add .
|
||||
|
||||
# Commit with clear message
|
||||
git commit -m "refactor: Move API credentials to environment variables
|
||||
|
||||
- Replace hardcoded Stripe API key with environment variable
|
||||
- Replace database password with AWS Secrets Manager reference
|
||||
- Add validation for required environment variables
|
||||
|
||||
Addresses: Secret exposure detected by Gitleaks scan"
|
||||
|
||||
# Push
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## Git History Cleanup
|
||||
|
||||
If secrets are in pushed commits, consider removing from git history.
|
||||
|
||||
### Decision Matrix
|
||||
|
||||
| Scenario | Action | Reason |
|
||||
|----------|--------|--------|
|
||||
| Public repo, secret exposed | **Mandatory** history rewrite | Secret is public knowledge |
|
||||
| Private repo, < 24 hours, < 5 collaborators | **Recommended** history rewrite | Minimal disruption |
|
||||
| Private repo, > 1 week, > 10 collaborators | **Optional** - Rotate only | High coordination cost |
|
||||
| Production repo with CI/CD | **Coordinate carefully** | May break automation |
|
||||
|
||||
### Method 1: git-filter-repo (Recommended)
|
||||
|
||||
Install:
|
||||
```bash
|
||||
pip install git-filter-repo
|
||||
```
|
||||
|
||||
Remove specific file from all history:
|
||||
```bash
|
||||
# Backup first
|
||||
git clone --mirror <repo-url> backup-repo.git
|
||||
|
||||
# Remove file
|
||||
git filter-repo --path config/secrets.yaml --invert-paths
|
||||
|
||||
# Force push
|
||||
git push origin --force --all
|
||||
```
|
||||
|
||||
Remove secrets matching pattern:
|
||||
```bash
|
||||
# Use callback for complex filtering
|
||||
git filter-repo --replace-text <(echo 'regex:sk_live_[a-zA-Z0-9]{24}==>REDACTED')
|
||||
```
|
||||
|
||||
### Method 2: BFG Repo-Cleaner
|
||||
|
||||
Download:
|
||||
```bash
|
||||
# macOS
|
||||
brew install bfg
|
||||
|
||||
# Or download JAR from https://rtyley.github.io/bfg-repo-cleaner/
|
||||
```
|
||||
|
||||
Remove specific file:
|
||||
```bash
|
||||
# Clone mirror
|
||||
git clone --mirror <repo-url> repo-mirror.git
|
||||
cd repo-mirror.git
|
||||
|
||||
# Remove file
|
||||
bfg --delete-files secrets.env
|
||||
|
||||
# Clean up
|
||||
git reflog expire --expire=now --all
|
||||
git gc --prune=now --aggressive
|
||||
|
||||
# Force push
|
||||
git push
|
||||
```
|
||||
|
||||
Remove secrets by pattern:
|
||||
```bash
|
||||
# Create replacements.txt
|
||||
echo "PASSWORD1==>***REMOVED***" > replacements.txt
|
||||
echo "sk_live_51ABC==>***REMOVED***" >> replacements.txt
|
||||
|
||||
# Run BFG
|
||||
bfg --replace-text replacements.txt repo-mirror.git
|
||||
```
|
||||
|
||||
### Method 3: Interactive Rebase (Small Changes)
|
||||
|
||||
For recent commits not yet widely distributed:
|
||||
|
||||
```bash
|
||||
# Rebase last N commits
|
||||
git rebase -i HEAD~5
|
||||
|
||||
# In editor, mark commits to 'edit'
|
||||
# When stopped at each commit:
|
||||
git rm config/secrets.yaml
|
||||
git commit --amend --no-edit
|
||||
git rebase --continue
|
||||
|
||||
# Force push
|
||||
git push --force-with-lease
|
||||
```
|
||||
|
||||
### Post-Rewrite Coordination
|
||||
|
||||
After rewriting history:
|
||||
|
||||
1. **Notify team immediately**:
|
||||
```text
|
||||
URGENT: Git history rewritten to remove exposed credentials.
|
||||
|
||||
Action required for all developers:
|
||||
1. Commit/stash any local changes
|
||||
2. Run: git fetch origin && git reset --hard origin/main
|
||||
3. Delete and re-clone if issues persist
|
||||
|
||||
Contact security team with questions.
|
||||
```
|
||||
|
||||
2. **Update CI/CD**:
|
||||
- Invalidate old caches
|
||||
- May need to reconfigure webhooks
|
||||
- Update any hardcoded commit references
|
||||
|
||||
3. **Update branch protection**:
|
||||
- May need to temporarily disable
|
||||
- Re-enable after force push completes
|
||||
|
||||
## Cloud Provider Specific
|
||||
|
||||
### AWS
|
||||
|
||||
**Check for unauthorized access**:
|
||||
```bash
|
||||
# List recent API calls for access key
|
||||
aws cloudtrail lookup-events \
|
||||
--lookup-attributes AttributeKey=Username,AttributeValue=compromised-user \
|
||||
--max-results 50 \
|
||||
--start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S)
|
||||
```
|
||||
|
||||
**Revoke all sessions**:
|
||||
```bash
|
||||
# Attach policy to deny all actions
|
||||
aws iam put-user-policy \
|
||||
--user-name compromised-user \
|
||||
--policy-name DenyAll \
|
||||
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*"}]}'
|
||||
```
|
||||
|
||||
### GCP
|
||||
|
||||
**Check audit logs**:
|
||||
```bash
|
||||
gcloud logging read "protoPayload.authenticationInfo.principalEmail=SERVICE_ACCOUNT_EMAIL" \
|
||||
--limit 100 \
|
||||
--format json
|
||||
```
|
||||
|
||||
**Disable service account**:
|
||||
```bash
|
||||
gcloud iam service-accounts disable SERVICE_ACCOUNT_EMAIL
|
||||
```
|
||||
|
||||
### Azure
|
||||
|
||||
**Review activity logs**:
|
||||
```bash
|
||||
az monitor activity-log list \
|
||||
--start-time 2024-01-01T00:00:00Z \
|
||||
--resource-id /subscriptions/SUBSCRIPTION_ID
|
||||
```
|
||||
|
||||
**Revoke access**:
|
||||
```bash
|
||||
# Regenerate keys
|
||||
az storage account keys renew \
|
||||
--account-name STORAGE_ACCOUNT \
|
||||
--key primary
|
||||
```
|
||||
|
||||
## Database Credentials
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
```sql
|
||||
-- Change password
|
||||
ALTER USER app_user WITH PASSWORD 'new_secure_password';
|
||||
|
||||
-- View recent connections
|
||||
SELECT datname, usename, client_addr, backend_start
|
||||
FROM pg_stat_activity
|
||||
WHERE usename = 'app_user'
|
||||
ORDER BY backend_start DESC;
|
||||
|
||||
-- Kill active connections (if suspicious)
|
||||
SELECT pg_terminate_backend(pid)
|
||||
FROM pg_stat_activity
|
||||
WHERE usename = 'app_user' AND client_addr != 'trusted_ip';
|
||||
```
|
||||
|
||||
### MySQL
|
||||
|
||||
```sql
|
||||
-- Change password
|
||||
ALTER USER 'app_user'@'%' IDENTIFIED BY 'new_secure_password';
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
-- View recent connections
|
||||
SELECT * FROM information_schema.PROCESSLIST
|
||||
WHERE USER = 'app_user';
|
||||
|
||||
-- Kill connections
|
||||
KILL CONNECTION process_id;
|
||||
```
|
||||
|
||||
### MongoDB
|
||||
|
||||
```javascript
|
||||
// Change password
|
||||
use admin
|
||||
db.changeUserPassword("app_user", "new_secure_password")
|
||||
|
||||
// View recent operations
|
||||
db.currentOp({ "active": true })
|
||||
|
||||
// Kill operation
|
||||
db.killOp(opid)
|
||||
```
|
||||
|
||||
## API Keys and Tokens
|
||||
|
||||
### GitHub
|
||||
|
||||
**Audit unauthorized access**:
|
||||
```bash
|
||||
# List recent events for token
|
||||
gh api /users/{username}/events/public | jq '.[] | {type, repo: .repo.name, created_at}'
|
||||
```
|
||||
|
||||
**Revoke all tokens** (if compromised account):
|
||||
1. Settings > Developer settings > Personal access tokens
|
||||
2. Select all tokens
|
||||
3. Click "Delete"
|
||||
|
||||
### Slack
|
||||
|
||||
**Check workspace audit logs**:
|
||||
1. Go to workspace settings (admin required)
|
||||
2. Navigate to Logs > Audit Logs
|
||||
3. Filter by token usage
|
||||
|
||||
**Regenerate token**:
|
||||
1. Go to api.slack.com/apps
|
||||
2. Select your app
|
||||
3. Navigate to OAuth & Permissions
|
||||
4. Click "Regenerate" on token
|
||||
|
||||
## Post-Remediation
|
||||
|
||||
### 1. Implement Prevention
|
||||
|
||||
**Pre-commit hooks**:
|
||||
```bash
|
||||
# Install Gitleaks pre-commit hook
|
||||
cd /path/to/repo
|
||||
cat << 'EOF' > .git/hooks/pre-commit
|
||||
#!/bin/sh
|
||||
gitleaks protect --verbose --redact --staged
|
||||
EOF
|
||||
chmod +x .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
**CI/CD checks**:
|
||||
```yaml
|
||||
# .github/workflows/secrets-scan.yml
|
||||
name: Secret Scanning
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
### 2. Update Secret Management
|
||||
|
||||
Migrate to proper secret management:
|
||||
|
||||
**Environment variables** (minimal):
|
||||
```bash
|
||||
# .env (never commit!)
|
||||
DATABASE_URL=postgresql://user:pass@host:5432/db
|
||||
API_KEY=sk_live_...
|
||||
|
||||
# .gitignore
|
||||
.env
|
||||
.env.local
|
||||
```
|
||||
|
||||
**Secret management services**:
|
||||
- AWS: Secrets Manager, Systems Manager Parameter Store
|
||||
- GCP: Secret Manager
|
||||
- Azure: Key Vault
|
||||
- HashiCorp: Vault
|
||||
- Kubernetes: Secrets
|
||||
|
||||
### 3. Document Incident
|
||||
|
||||
Create incident report including:
|
||||
- **Timeline**: When secret was committed, detected, remediated
|
||||
- **Exposure**: Duration, repository visibility, access scope
|
||||
- **Impact**: Systems accessed, data at risk, unauthorized activity
|
||||
- **Response**: Rotation completed, logs reviewed, history cleaned
|
||||
- **Prevention**: Controls implemented to prevent recurrence
|
||||
|
||||
### 4. Team Training
|
||||
|
||||
Conduct training on:
|
||||
- Using environment variables and secret management
|
||||
- Pre-commit hooks and local scanning
|
||||
- Recognizing secrets in code review
|
||||
- Incident response procedures
|
||||
|
||||
### 5. Compliance Notifications
|
||||
|
||||
If required by regulations:
|
||||
- **GDPR**: Notify supervisory authority within 72 hours if personal data at risk
|
||||
- **PCI-DSS**: Notify card brands and processor if payment data affected
|
||||
- **SOC2**: Document in compliance report, may trigger audit
|
||||
- **HIPAA**: Notify covered entities if PHI exposed
|
||||
|
||||
## Prevention Checklist
|
||||
|
||||
- [ ] Credential rotated and old credential deactivated
|
||||
- [ ] Access logs reviewed for unauthorized activity
|
||||
- [ ] Secret removed from current code
|
||||
- [ ] Git history cleaned (if applicable)
|
||||
- [ ] Team notified of credential change
|
||||
- [ ] Applications updated with new credential
|
||||
- [ ] Pre-commit hooks installed
|
||||
- [ ] CI/CD secret scanning enabled
|
||||
- [ ] Secret management solution implemented
|
||||
- [ ] Incident documented
|
||||
- [ ] Compliance notifications sent (if required)
|
||||
- [ ] Team training scheduled
|
||||
|
||||
## Emergency Contacts
|
||||
|
||||
Maintain contact list for rapid response:
|
||||
- **Security Team**: security@company.com
|
||||
- **DevOps On-Call**: devops-oncall@company.com
|
||||
- **Cloud Provider Support**: Account-specific
|
||||
- **Compliance Officer**: compliance@company.com
|
||||
Reference in New Issue
Block a user