392 lines
9.7 KiB
Markdown
392 lines
9.7 KiB
Markdown
# Security Checklist for Production Deployment
|
|
|
|
This checklist helps ensure your LinkForty deployment on Fly.io follows security best practices.
|
|
|
|
## ✅ Pre-Deployment Security Checklist
|
|
|
|
### Environment Variables & Secrets
|
|
|
|
- [ ] All sensitive values are set using `fly secrets set` (never in `fly.toml`)
|
|
- [ ] `DATABASE_URL` includes `?sslmode=require` for PostgreSQL
|
|
- [ ] `REDIS_URL` includes authentication credentials
|
|
- [ ] `CORS_ORIGIN` is set to your actual frontend domain(s), not `*`
|
|
- [ ] `NODE_ENV` is set to `"production"`
|
|
- [ ] No secrets are committed to version control
|
|
- [ ] `.env` files are in `.gitignore`
|
|
|
|
### Database Security
|
|
|
|
- [ ] PostgreSQL uses SSL/TLS connections (`sslmode=require`)
|
|
- [ ] Database password is strong (20+ characters, random)
|
|
- [ ] Database is not publicly accessible (Fly Postgres is private by default)
|
|
- [ ] Connection pooling is configured (default: 2-10 connections)
|
|
- [ ] Database backups are enabled (see [fly.postgres.md](./fly.postgres.md))
|
|
- [ ] Backup retention policy is configured
|
|
|
|
### Redis Security
|
|
|
|
- [ ] Redis requires authentication (Upstash Redis includes this by default)
|
|
- [ ] Redis connection uses TLS (`rediss://` protocol)
|
|
- [ ] Redis is not publicly accessible
|
|
- [ ] Connection timeout is configured
|
|
|
|
### Application Security
|
|
|
|
- [ ] CORS is properly configured (not set to `*` in production)
|
|
- [ ] Rate limiting is enabled for link creation endpoints
|
|
- [ ] Input validation is active (Zod schemas)
|
|
- [ ] SQL injection protection via parameterized queries (pg library handles this)
|
|
- [ ] Health check endpoint (`/health`) exposes no sensitive data
|
|
- [ ] Error messages don't leak sensitive information
|
|
- [ ] Logging doesn't include secrets or PII
|
|
|
|
### Network Security
|
|
|
|
- [ ] HTTPS is enforced (`force_https = true` in fly.toml)
|
|
- [ ] HTTP is redirected to HTTPS
|
|
- [ ] Health checks use HTTPS
|
|
- [ ] No sensitive services are exposed publicly
|
|
|
|
### Access Control
|
|
|
|
- [ ] Fly.io account uses strong password + 2FA
|
|
- [ ] Fly.io organization access is limited to required team members
|
|
- [ ] Deploy tokens (if used in CI/CD) have minimum required permissions
|
|
- [ ] Database credentials are rotated regularly (quarterly minimum)
|
|
|
|
---
|
|
|
|
## 🔐 Security Configuration Details
|
|
|
|
### 1. Database URL Security
|
|
|
|
Your `DATABASE_URL` should look like:
|
|
```
|
|
postgresql://user:password@host:5432/dbname?sslmode=require
|
|
```
|
|
|
|
Key requirements:
|
|
- `sslmode=require` - Forces SSL/TLS encryption
|
|
- Strong password (20+ characters)
|
|
- Host should be internal Fly network (`.internal` domain) if using Fly Postgres
|
|
|
|
Set it securely:
|
|
```bash
|
|
fly secrets set DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require"
|
|
```
|
|
|
|
### 2. Redis URL Security
|
|
|
|
Your `REDIS_URL` should look like:
|
|
```
|
|
rediss://default:password@host:6379
|
|
```
|
|
|
|
Key requirements:
|
|
- `rediss://` protocol (TLS encrypted)
|
|
- Authentication password included
|
|
- Upstash Redis (recommended) includes TLS by default
|
|
|
|
Set it securely:
|
|
```bash
|
|
fly secrets set REDIS_URL="rediss://default:password@host:6379"
|
|
```
|
|
|
|
### 3. CORS Configuration
|
|
|
|
For production, set specific origins:
|
|
|
|
```bash
|
|
# Single origin
|
|
fly secrets set CORS_ORIGIN="https://yourdomain.com"
|
|
|
|
# Multiple origins (comma-separated)
|
|
fly secrets set CORS_ORIGIN="https://yourdomain.com,https://app.yourdomain.com"
|
|
```
|
|
|
|
Never use `*` in production - this allows any website to make requests to your API.
|
|
|
|
### 4. Rate Limiting
|
|
|
|
LinkForty includes built-in rate limiting. Verify it's enabled in your deployment:
|
|
|
|
- Link creation: Limited by IP address
|
|
- Analytics queries: Limited by userId
|
|
- Redirect endpoints: Unlimited (by design for fast redirects)
|
|
|
|
Monitor rate limit metrics:
|
|
```bash
|
|
fly logs | grep "rate limit"
|
|
```
|
|
|
|
### 5. Input Validation
|
|
|
|
LinkForty uses Zod for input validation. Ensure validation errors are logged:
|
|
|
|
```bash
|
|
fly logs | grep "validation"
|
|
```
|
|
|
|
Common validation issues to monitor:
|
|
- Invalid URLs
|
|
- Malformed userId
|
|
- Invalid UTM parameters
|
|
- Expired links
|
|
|
|
---
|
|
|
|
## 🔄 Secret Rotation Policy
|
|
|
|
Rotate secrets regularly to minimize compromise risk.
|
|
|
|
### Quarterly Rotation (Every 3 months)
|
|
|
|
1. **Database Password**
|
|
```bash
|
|
# On Fly Postgres
|
|
fly postgres connect -a linkforty-db
|
|
ALTER USER your_user WITH PASSWORD 'new-strong-password';
|
|
\q
|
|
|
|
# Update secret
|
|
fly secrets set DATABASE_URL="postgresql://user:new-password@host:5432/dbname?sslmode=require"
|
|
```
|
|
|
|
2. **Redis Password**
|
|
```bash
|
|
# Generate new Upstash Redis credentials
|
|
# In Upstash dashboard: Reset password
|
|
|
|
# Update secret
|
|
fly secrets set REDIS_URL="rediss://default:new-password@host:6379"
|
|
```
|
|
|
|
3. **API Keys** (if you add API authentication)
|
|
```bash
|
|
fly secrets set API_KEY="new-random-key"
|
|
```
|
|
|
|
### After Security Incident
|
|
|
|
Rotate ALL secrets immediately:
|
|
- Database credentials
|
|
- Redis credentials
|
|
- Any API keys
|
|
- Fly.io deploy tokens (if compromised)
|
|
|
|
---
|
|
|
|
## 🔍 Security Monitoring
|
|
|
|
### Log Monitoring
|
|
|
|
Monitor these security-relevant events:
|
|
|
|
```bash
|
|
# Failed database connections
|
|
fly logs | grep "connection refused"
|
|
|
|
# Rate limit hits
|
|
fly logs | grep "rate limit"
|
|
|
|
# Validation errors
|
|
fly logs | grep "validation error"
|
|
|
|
# Suspicious activity patterns
|
|
fly logs | grep "error"
|
|
```
|
|
|
|
### Metrics to Watch
|
|
|
|
- **Unusual traffic spikes** - Potential DDoS or abuse
|
|
- **High error rates** - Potential attack or misconfiguration
|
|
- **Database connection errors** - Credential issues or network problems
|
|
- **Redis connection failures** - Service degradation
|
|
|
|
Access metrics:
|
|
```bash
|
|
fly dashboard # Web UI with graphs
|
|
fly status # Current health
|
|
```
|
|
|
|
### Automated Alerts (Recommended)
|
|
|
|
Set up alerts for:
|
|
- High error rates (>5% of requests)
|
|
- Service downtime
|
|
- Database connection failures
|
|
- Unusual traffic patterns
|
|
|
|
Fly.io integrates with:
|
|
- Sentry (error tracking)
|
|
- Datadog (monitoring)
|
|
- Prometheus (metrics)
|
|
|
|
---
|
|
|
|
## 🛡️ Incident Response Plan
|
|
|
|
### If Secrets Are Compromised
|
|
|
|
1. **Immediately rotate all secrets**
|
|
```bash
|
|
# See "Secret Rotation Policy" section above
|
|
```
|
|
|
|
2. **Review access logs**
|
|
```bash
|
|
fly logs --all
|
|
```
|
|
|
|
3. **Check for unauthorized database changes**
|
|
```bash
|
|
fly postgres connect -a linkforty-db
|
|
SELECT * FROM users ORDER BY created_at DESC LIMIT 100;
|
|
SELECT * FROM links ORDER BY created_at DESC LIMIT 100;
|
|
```
|
|
|
|
4. **Review Fly.io access logs**
|
|
- Check Fly.io dashboard for recent deploys
|
|
- Review team member access
|
|
- Check for unknown IP addresses
|
|
|
|
5. **Deploy with new secrets**
|
|
```bash
|
|
fly deploy
|
|
```
|
|
|
|
6. **Document the incident**
|
|
- What was compromised?
|
|
- How was it discovered?
|
|
- What actions were taken?
|
|
- How to prevent in the future?
|
|
|
|
### If Database Is Compromised
|
|
|
|
1. **Restore from backup**
|
|
```bash
|
|
# See fly.postgres.md for backup/restore instructions
|
|
```
|
|
|
|
2. **Analyze what data was accessed/modified**
|
|
|
|
3. **Notify affected users** (if PII was compromised)
|
|
|
|
4. **Review and strengthen security measures**
|
|
|
|
---
|
|
|
|
## 🔒 Data Protection
|
|
|
|
### Personal Identifiable Information (PII)
|
|
|
|
LinkForty collects:
|
|
- IP addresses (for geolocation)
|
|
- User agent strings (for device detection)
|
|
- Referrer URLs (for analytics)
|
|
- User IDs (provided by you)
|
|
|
|
**Your responsibilities:**
|
|
- Comply with GDPR, CCPA, or relevant privacy laws
|
|
- Implement data retention policies
|
|
- Provide user data deletion endpoints
|
|
- Maintain privacy policy
|
|
- Obtain user consent where required
|
|
|
|
### Data Retention
|
|
|
|
Consider implementing:
|
|
- Automatic deletion of click events older than X days
|
|
- User data export functionality
|
|
- Data anonymization for old analytics
|
|
|
|
Example cleanup query:
|
|
```sql
|
|
DELETE FROM click_events WHERE created_at < NOW() - INTERVAL '90 days';
|
|
```
|
|
|
|
Schedule via cron or Fly.io scheduled tasks.
|
|
|
|
### Encryption
|
|
|
|
- **In transit:** HTTPS/TLS for all connections (enforced by Fly.io)
|
|
- **At rest:** Fly Postgres encrypts data at rest automatically
|
|
- **Application level:** Consider encrypting sensitive user data in JSONB fields
|
|
|
|
---
|
|
|
|
## 🚨 Security Vulnerabilities
|
|
|
|
### Reporting Security Issues
|
|
|
|
**DO NOT** open public GitHub issues for security vulnerabilities.
|
|
|
|
Instead:
|
|
- Email security@yourdomain.com (set up a security contact)
|
|
- Use GitHub Security Advisories (private disclosure)
|
|
- Allow 90 days for responsible disclosure
|
|
|
|
### Keeping Dependencies Updated
|
|
|
|
```bash
|
|
# Check for vulnerabilities
|
|
npm audit
|
|
|
|
# Fix automatically where possible
|
|
npm audit fix
|
|
|
|
# Review and update dependencies quarterly
|
|
npm outdated
|
|
npm update
|
|
```
|
|
|
|
Subscribe to security advisories:
|
|
- Node.js security releases
|
|
- Fastify security updates
|
|
- PostgreSQL security announcements
|
|
|
|
---
|
|
|
|
## ✅ Security Checklist Summary
|
|
|
|
Print and complete this checklist before every production deployment:
|
|
|
|
**Pre-Deploy:**
|
|
- [ ] Secrets set via `fly secrets set` (not in code)
|
|
- [ ] Database uses SSL (`sslmode=require`)
|
|
- [ ] CORS configured for specific domains
|
|
- [ ] HTTPS enforced in `fly.toml`
|
|
- [ ] No secrets in version control
|
|
|
|
**Post-Deploy:**
|
|
- [ ] Health check passes
|
|
- [ ] HTTPS redirect works
|
|
- [ ] CORS allows only intended domains
|
|
- [ ] Database connection successful
|
|
- [ ] Redis connection successful
|
|
- [ ] Rate limiting active
|
|
- [ ] Error logging working
|
|
|
|
**Ongoing:**
|
|
- [ ] Secrets rotated quarterly
|
|
- [ ] Dependencies updated monthly
|
|
- [ ] Logs monitored weekly
|
|
- [ ] Backups verified monthly
|
|
- [ ] Access control reviewed quarterly
|
|
|
|
---
|
|
|
|
## 📚 Additional Resources
|
|
|
|
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
|
- [Fly.io Security Best Practices](https://fly.io/docs/reference/security/)
|
|
- [PostgreSQL Security](https://www.postgresql.org/docs/current/runtime-config-connection.html#RUNTIME-CONFIG-CONNECTION-SSL)
|
|
- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)
|
|
- [Fastify Security](https://www.fastify.io/docs/latest/Guides/Security/)
|
|
|
|
---
|
|
|
|
**Last Updated:** 2025-01-13
|
|
|
|
Review and update this checklist quarterly or after any security incident.
|