9.7 KiB
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 infly.toml) DATABASE_URLincludes?sslmode=requirefor PostgreSQLREDIS_URLincludes authentication credentialsCORS_ORIGINis set to your actual frontend domain(s), not*NODE_ENVis set to"production"- No secrets are committed to version control
.envfiles 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)
- 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 = truein 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 (
.internaldomain) if using Fly Postgres
Set it securely:
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:
fly secrets set REDIS_URL="rediss://default:password@host:6379"
3. CORS Configuration
For production, set specific origins:
# 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:
fly logs | grep "rate limit"
5. Input Validation
LinkForty uses Zod for input validation. Ensure validation errors are logged:
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)
-
Database Password
# 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" -
Redis Password
# Generate new Upstash Redis credentials # In Upstash dashboard: Reset password # Update secret fly secrets set REDIS_URL="rediss://default:new-password@host:6379" -
API Keys (if you add API authentication)
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:
# 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:
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
-
Immediately rotate all secrets
# See "Secret Rotation Policy" section above -
Review access logs
fly logs --all -
Check for unauthorized database changes
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; -
Review Fly.io access logs
- Check Fly.io dashboard for recent deploys
- Review team member access
- Check for unknown IP addresses
-
Deploy with new secrets
fly deploy -
Document the incident
- What was compromised?
- How was it discovered?
- What actions were taken?
- How to prevent in the future?
If Database Is Compromised
-
Restore from backup
# See fly.postgres.md for backup/restore instructions -
Analyze what data was accessed/modified
-
Notify affected users (if PII was compromised)
-
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:
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
# 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
- Fly.io Security Best Practices
- PostgreSQL Security
- Node.js Security Best Practices
- Fastify Security
Last Updated: 2025-01-13
Review and update this checklist quarterly or after any security incident.