Security Scrubbing Knowledge
LLM Provider API Keys
Key Patterns by Provider
| Provider | Pattern | Environment Variable |
|---|---|---|
| OpenAI | sk-..., sk-proj-..., sk-svcacct-... | OPENAI_API_KEY |
| Anthropic | sk-ant-... | ANTHROPIC_API_KEY |
| Google AI | AIza[0-9A-Za-z-_]{35} | GOOGLE_API_KEY, GEMINI_API_KEY |
| Cohere | 40-char alphanumeric | COHERE_API_KEY |
| Mistral | 32-char alphanumeric | MISTRAL_API_KEY |
| Groq | gsk_... | GROQ_API_KEY |
| Perplexity | pplx-... | PERPLEXITY_API_KEY |
| Hugging Face | hf_... | HF_TOKEN, HUGGINGFACE_API_KEY |
| Replicate | r8_... | REPLICATE_API_TOKEN |
| Together AI | 64-char hex | TOGETHER_API_KEY |
| xAI (Grok) | varies | XAI_API_KEY |
Secure API Key Usage
Never do this:
# BAD - hardcoded key
client = OpenAI(api_key="sk-abc123...")
Do this instead:
# GOOD - environment variable
import os
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
Or use dotenv:
from dotenv import load_dotenv
load_dotenv() # Loads from .env file
client = OpenAI() # Auto-reads OPENAI_API_KEY
Other Common Secret Patterns
| Service | Pattern | Example |
|---|---|---|
| AWS Access Key | AKIA[0-9A-Z]{16} | AKIAIOSFODNN7EXAMPLE |
| AWS Secret Key | 40 char base64 | wJalrXUtnFEMI/K7MDENG... |
| GitHub Token | ghp_[a-zA-Z0-9]{36} | ghp_xxxx... |
| GitHub PAT | github_pat_[0-9a-zA-Z_]{82} | github_pat_... |
| GitLab Token | glpat-[0-9a-zA-Z-]{20} | glpat-xxxx... |
| Stripe Live | sk_live_[a-zA-Z0-9]{24} | sk_live_... |
| Stripe Test | sk_test_[a-zA-Z0-9]{24} | sk_test_... |
| Slack Token | xox[baprs]-... | xoxb-... |
| Slack Webhook | https://hooks.slack.com/... | webhook URL |
| Twilio | SK[0-9a-fA-F]{32} | SK... |
| SendGrid | SG.[a-zA-Z0-9]{22}.[a-zA-Z0-9]{43} | SG.... |
| Discord Bot | [MN][A-Za-z0-9]{23,}... | Bot token |
| Firebase | AAAA[A-Za-z0-9_-]{7}:... | FCM key |
| Supabase | sbp_[a-f0-9]{40} | sbp_... |
| JWT | eyJ... (3 base64 segments) | eyJhbGc... |
| Private Key | -----BEGIN...PRIVATE KEY----- | PEM format |
Credential Guard
The scrub plugin includes a PreToolUse hook that blocks Claude from reading files containing credentials.
How It Works
- Hook intercepts
Readoperations - Scans target file for 40+ credential patterns
- Blocks read if secrets detected
- Allows if file is in allowlist
Allowlist
Add trusted files to ~/.claude/plugins/scrub/.guard-allowlist:
# Allow test fixtures
tests/fixtures/
*.test.js
.env.example
Commands
/scrub:guard status # Check if enabled
/scrub:guard on # Enable guard
/scrub:guard off # Disable guard
/scrub:guard allowlist # Manage allowlist
Where Secrets Hide
- Configuration files:
.env,config.js,settings.py - Test files: Hardcoded test credentials
- Git history: Removed from HEAD but still in commits
- Logs: Accidentally logged tokens
- Comments: "Temporary" credentials
- CI/CD files:
.github/workflows/,Jenkinsfile - Notebooks: Jupyter notebooks with inline keys
- Docker files:
Dockerfile,docker-compose.yml
PII Categories
Identifiable Data
- Direct identifiers: Name, SSN, passport number
- Contact info: Email, phone, address
- Network info: IP address, MAC address
- Financial: Credit card, bank account
- Biometric: Fingerprints, face data
Log Sanitization
Before sharing logs:
- Remove IP addresses
- Redact email addresses
- Mask user IDs
- Strip session tokens
- Remove query parameters with PII
Git History Security
When to Scrub History
- Accidentally committed credentials
- Pushed .env file
- Included private keys
- Added database dumps with PII
Tools
- git-filter-repo (recommended): Fast, safe, feature-rich
- git filter-branch: Built-in but slower
- BFG Repo Cleaner: Java-based, good for large repos
Post-Scrub Checklist
- Force push all branches
- Delete all tags and recreate
- Notify all collaborators
- Rotate ALL exposed credentials
- Clear CI/CD caches
- GitHub: Contact support to clear caches
File Permissions
Secure Defaults
| File Type | Recommended | Why |
|---|---|---|
| Private keys | 600 | Owner only |
| .env files | 600 | Contains secrets |
| Scripts | 755 | Execute for all, write for owner |
| Config files | 644 | Read for all, write for owner |
| Directories | 755 | Standard access |
Dangerous Permissions
- World-writable (o+w): Anyone can modify
- World-readable keys: Credentials exposed
- SetUID on scripts: Privilege escalation risk
.gitignore Security
Must-Have Entries
# Environment
.env
.env.*
.env.local
# Keys and certificates
*.pem
*.key
*.p12
*.pfx
*.keystore
id_rsa
id_ed25519
# Credentials
credentials.json
secrets.json
*secret*
*credential*
# Logs
*.log
logs/
Best Practices
- Never commit secrets - Use environment variables
- Use credential guard - Block secrets from reaching AI
- Pre-commit hooks - Scan before commit
- Least privilege - Minimal file permissions
- Rotate regularly - Even if not leaked
- Audit trails - Know who accessed what
- Defense in depth - Multiple protection layers
- .env.example - Document required vars without values
