Files
linkforty/infra/fly.io/SECURITY.md
T
2026-07-31 11:47:31 +09:00

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 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)
  • 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:

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)

  1. 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"
    
  2. 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"
    
  3. 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

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

    # See "Secret Rotation Policy" section above
    
  2. Review access logs

    fly logs --all
    
  3. 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;
    
  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

    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

    # 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:

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


Last Updated: 2025-01-13

Review and update this checklist quarterly or after any security incident.