Files
thrillwiki_django_no_react/docs/configuration/secret-management.md
pacnpal edcd8f2076 Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols.
- Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage.
- Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
2025-12-23 16:41:42 -05:00

8.0 KiB

Secret Management Guide

This guide covers best practices for managing secrets in ThrillWiki, including rotation procedures, integration with secret management services, and emergency procedures.

Overview

Secrets are sensitive configuration values that must be protected:

  • Database credentials
  • API keys and tokens
  • Encryption keys
  • Service passwords

ThrillWiki provides a flexible secret management system that can work with environment variables (development) or integrate with enterprise secret management services (production).

Secret Classification

Critical Secrets (Immediate rotation required if compromised)

Secret Impact Rotation Frequency
SECRET_KEY All cryptographic operations 90 days recommended
DATABASE_URL Database access On suspected breach
SENTRY_DSN Error tracking access On suspected breach

High-Priority Secrets

Secret Impact Rotation Frequency
CLOUDFLARE_IMAGES_API_TOKEN Image CDN access 180 days
FORWARD_EMAIL_API_KEY Email sending 180 days
TURNSTILE_SECRET_KEY CAPTCHA bypass 365 days

Service Secrets

Secret Impact Rotation Frequency
CLOUDFLARE_IMAGES_WEBHOOK_SECRET Webhook verification 365 days
REDIS_URL (with password) Cache access On suspected breach

Development Setup

For development, use a .env file:

# Copy the template
cp .env.example .env

# Generate a secure SECRET_KEY
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

# Edit .env with your values

Validation

Validate your configuration:

python manage.py validate_settings

Production Secret Management

Option 1: Environment Variables (Simple)

Set secrets as environment variables in your deployment platform:

# Heroku
heroku config:set SECRET_KEY="your-secret-key"

# Docker
docker run -e SECRET_KEY="your-secret-key" ...

# Kubernetes (from secret)
kubectl create secret generic thrillwiki-secrets \
  --from-literal=SECRET_KEY="your-secret-key"

Option 2: AWS Secrets Manager

For AWS deployments, integrate with Secrets Manager:

# In config/settings/secrets.py, implement:
from config.settings.secrets import SecretProvider

class AWSSecretsProvider(SecretProvider):
    def __init__(self, secret_name: str):
        import boto3
        self.client = boto3.client('secretsmanager')
        self.secret_name = secret_name
        self._cache = {}

    def get_secret(self, name: str) -> str:
        if not self._cache:
            response = self.client.get_secret_value(
                SecretId=self.secret_name
            )
            import json
            self._cache = json.loads(response['SecretString'])
        return self._cache.get(name)

# Usage in settings
from config.settings.secrets import set_secret_provider
set_secret_provider(AWSSecretsProvider('thrillwiki/production'))

Option 3: HashiCorp Vault

For Vault integration:

class VaultSecretsProvider(SecretProvider):
    def __init__(self, vault_addr: str, token: str, path: str):
        import hvac
        self.client = hvac.Client(url=vault_addr, token=token)
        self.path = path

    def get_secret(self, name: str) -> str:
        response = self.client.secrets.kv.read_secret_version(
            path=self.path
        )
        return response['data']['data'].get(name)

Secret Rotation Procedures

Rotating SECRET_KEY

Impact: All sessions will be invalidated. Users will need to log in again.

  1. Prepare the new key:

    python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
    
  2. Update deployment:

    # Update environment variable
    export SECRET_KEY="new-secret-key-here"
    export SECRET_KEY_VERSION="2"  # Increment version
    
  3. Deploy during low-traffic period

  4. Clear session cache:

    python manage.py clearsessions
    
  5. Monitor for issues:

    • Check error rates
    • Monitor login success rates
    • Watch for authentication errors

Rotating Database Credentials

Impact: Application downtime if not done carefully.

  1. Create new database user:

    CREATE USER thrillwiki_new WITH PASSWORD 'new-password';
    GRANT ALL PRIVILEGES ON DATABASE thrillwiki TO thrillwiki_new;
    
  2. Update application:

    export DATABASE_URL="postgis://thrillwiki_new:new-password@host:5432/thrillwiki"
    
  3. Deploy and verify

  4. Remove old user:

    DROP USER thrillwiki_old;
    

Rotating API Keys

For third-party API keys:

  1. Generate new key in the service's dashboard
  2. Update environment variable
  3. Deploy
  4. Revoke old key in the service's dashboard

Automated Rotation

Enable Rotation Monitoring

# In .env
SECRET_ROTATION_ENABLED=True
SECRET_KEY_VERSION=1
SECRET_EXPIRY_WARNING_DAYS=30

Check Rotation Status

python manage.py validate_settings --secrets-only

Automated Alerts

Configure logging to alert on expiry warnings:

# In your monitoring setup, watch for:
# Logger: security
# Level: WARNING
# Message contains: "Secret expiry"

Emergency Procedures

Suspected Credential Compromise

  1. Immediate Actions:

    • Rotate the compromised credential immediately
    • Check access logs for unauthorized use
    • Review related systems for compromise
  2. Investigation:

    • Identify the source of the leak
    • Review access patterns
    • Document the incident
  3. Remediation:

    • Fix the vulnerability that caused the leak
    • Update security procedures
    • Conduct a post-mortem

Complete Secret Rotation (Breach Response)

If a major breach is suspected, rotate all secrets:

# 1. Generate new SECRET_KEY
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

# 2. Create new database credentials
# 3. Regenerate all API keys in respective dashboards
# 4. Update all environment variables
# 5. Deploy
# 6. Clear all caches
python manage.py clear_cache  # If you have this command
redis-cli FLUSHALL  # Or clear Redis directly

# 7. Invalidate all sessions
python manage.py clearsessions

# 8. Revoke all old credentials

Security Best Practices

Do's

  • Use different secrets for each environment
  • Rotate secrets regularly (90-180 days)
  • Use secret management services in production
  • Monitor for secret exposure (git-secrets, truffleHog)
  • Log secret access for audit trails
  • Use strong, randomly generated secrets

Don'ts

  • Never commit secrets to version control
  • Never share secrets via chat or email
  • Never use the same secret across environments
  • Never use default or example secrets in production
  • Never log secret values (even partially)

Audit and Compliance

Access Logging

Secret access is logged to the security logger:

# Logged events:
# - Secret validation failures
# - Secret rotation warnings
# - Invalid secret format detections

Regular Audits

Monthly security checklist:

  • Review secret rotation schedule
  • Check for exposed secrets in logs
  • Verify secret strength requirements
  • Audit secret access patterns
  • Test secret rotation procedures

Troubleshooting

"Secret validation failed"

# Check the specific error
python manage.py validate_settings

# Common issues:
# - Secret too short
# - Placeholder value detected
# - Missing required secret

"SECRET_KEY does not meet requirements"

# Generate a proper key
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

Secret provider errors

# Test secret provider connectivity
python -c "
from config.settings.secrets import get_secret_provider
provider = get_secret_provider()
print(provider.list_secrets())
"