API Key Security Best Practices
The mistakes are predictable. The fixes are too.
TL;DR
Store API keys in environment variables, never in code. Use separate keys for dev and production. Limit each key to only what it needs. Rotate critical keys every 90 days, compromised ones immediately. Monitor usage for anomalies; a sudden spike is almost always bad news. And write down your rotation process before an incident forces you to improvise it.
1. Storage Best Practices
Do This
- Store keys in environment variables
- Use your hosting platform's secret management
- Keep production keys separate from development keys
- Use a .env.example file to document required variables
Never Do This
- Hardcode keys in source files
- Commit .env files to git
- Store keys in frontend/client-side code
- Share keys via Slack, email, or other chat tools
- Use the same key for development and production
// BAD: Hardcoded key
const stripe = new Stripe('sk_live_xxxxx');
// GOOD: Environment variable
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// BETTER: With validation
const stripeKey = process.env.STRIPE_SECRET_KEY;
if (!stripeKey) {
throw new Error('STRIPE_SECRET_KEY is required');
}
const stripe = new Stripe(stripeKey);
2. Key Scoping and Permissions
Give each key the minimum permissions it needs to do its job. That's it. Here's how that looks across common services:
| Approach | Risk Level | Recommendation |
|---|---|---|
| Full access key | High | Avoid if possible |
| Read-only key | Medium | Use when only reading data |
| Scoped key (specific resources) | Low | Preferred approach |
| Time-limited key | Lowest | Use for CI/CD, one-time tasks |
Service-Specific Examples
# Stripe: Use restricted keys
# Dashboard > Developers > API keys > Create restricted key
# Only enable permissions needed (e.g., "Charges: Read" for analytics)
# AWS: Use IAM policies
# Create policies with specific actions and resources
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}
# GitHub: Use fine-grained PATs
# Settings > Developer settings > Fine-grained tokens
# Select specific repositories and permissions
3. Key Rotation Strategy
| Key Type | Rotation Frequency | Why |
|---|---|---|
| Payment keys (Stripe, etc.) | 90 days | High financial risk |
| Database credentials | 90 days | Data access risk |
| Third-party API keys | 180 days | Moderate risk |
| Public/anon keys | As needed | Low risk if RLS enabled |
| Compromised keys | Immediately | Active threat |
Rotation Checklist
Before Rotating
- Generate new key in service dashboard
- Document the new key securely
- Prepare environment variable updates
During Rotation
- Update production environment variables
- Deploy or restart affected services
- Verify new key is working
- Revoke old key
After Rotating
- Update local development environments
- Notify team members
- Update any documentation
- Schedule next rotation
4. Monitoring and Alerting
Catching a compromised key early is the difference between a 10-minute rotation and a surprise billing spike. Watch for:
- Usage alerts: Set thresholds for API calls (e.g., alert if OpenAI usage exceeds $100/day)
- Geographic alerts: Flag API calls from unexpected locations
- Rate anomalies: Sudden spikes in usage often indicate compromise
- Failed auth attempts: Multiple failures may indicate key scanning
Service-Specific Monitoring
# OpenAI: Set usage limits
# Settings > Limits > Set monthly budget and alerts
# Stripe: Enable webhook for suspicious activity
# Dashboard > Developers > Webhooks > radar.early_fraud_warning
# AWS: CloudWatch alarms
# Set billing alarms and API call anomaly detection
5. Incident Response Plan
Don't write this plan during the incident. Write it now:
- Detect: How did you learn the key was compromised?
- Contain: Immediately rotate or revoke the key
- Assess: Check logs for unauthorized usage
- Remediate: Deploy new key, fix the exposure source
- Review: Document what happened and prevent recurrence
Speed Matters
Automated scanners find exposed keys within minutes. AWS keys have been used for crypto mining within 5 minutes of exposure. Have your rotation process documented so anyone on the team can execute it quickly.
6. Development vs Production
| Environment | Key Type | Example |
|---|---|---|
| Development | Test/sandbox keys | sk_test_xxxxx |
| Staging | Test keys | sk_test_xxxxx |
| Production | Live keys | sk_live_xxxxx |
Configure your CI/CD to use separate environment variable sets for each environment. Production keys don't belong in your dev setup, ever.
Should I encrypt API keys at rest?
If you're using environment variables in a trusted hosting platform (Vercel, Railway, etc.), they're already encrypted at rest. For self-hosted solutions, use a secrets manager like HashiCorp Vault or AWS Secrets Manager.
How do I share API keys with team members?
Use your hosting platform's team features (Vercel team settings, etc.) or a secrets manager. Don't share via Slack, email, or shared documents. If you have to share directly, use encrypted channels and rotate immediately after onboarding.
Are Supabase anon keys really safe to expose?
Supabase anon keys are designed to be public when used with Row Level Security (RLS). They're similar to Firebase's public config. The key identifies your project; RLS policies control what anyone can do with it. The service_role key is a different story: it has to stay secret.
Related guides:How to Hide API Keys · How to Rotate API Keys · How to Enable Secret Scanning