#!/usr/bin/env python3 """ Security Hardening Script for Church Music Database Implements critical security fixes """ import os import secrets import sys import re from pathlib import Path def generate_secure_key(): """Generate cryptographically secure secret key""" return secrets.token_hex(32) def check_env_file_security(env_path): """Check if .env file has secure permissions""" if not os.path.exists(env_path): return False, "File does not exist" stat_info = os.stat(env_path) mode = stat_info.st_mode & 0o777 if mode != 0o600: return False, f"Insecure permissions: {oct(mode)}. Should be 0600" return True, "OK" def secure_env_file(env_path): """Set secure permissions on .env file""" try: os.chmod(env_path, 0o600) return True except Exception as e: return False, str(e) def validate_postgresql_uri(uri): """Validate PostgreSQL connection string""" if not uri or uri == "": return False, "Empty URI" # Check for default/weak passwords weak_patterns = [ 'your_password', 'password', 'admin', '123456', 'postgres' ] for pattern in weak_patterns: if pattern.lower() in uri.lower(): return False, f"Weak/default password detected: {pattern}" # Validate format if not uri.startswith('postgresql://'): return False, "Invalid PostgreSQL URI format" return True, "OK" def main(): print("╔══════════════════════════════════════════════════════════════╗") print("║ SECURITY HARDENING - Critical Fixes ║") print("╚══════════════════════════════════════════════════════════════╝") print() project_root = Path(__file__).parent backend_dir = project_root / "backend" env_file = backend_dir / ".env" issues_found = [] fixes_applied = [] # Check 1: .env file exists and has secure permissions print("🔒 Checking .env file security...") if env_file.exists(): is_secure, msg = check_env_file_security(env_file) if not is_secure: issues_found.append(f".env file: {msg}") if secure_env_file(env_file): fixes_applied.append("Set .env permissions to 0600") print(" ✓ Fixed: Set secure permissions (0600)") else: print(f" ✗ Failed to secure .env file") else: print(" ✓ .env file has secure permissions") else: issues_found.append(".env file does not exist") print(" ⚠ .env file not found. Use .env.template to create one.") # Check 2: SECRET_KEY strength print("\n🔑 Checking SECRET_KEY...") if env_file.exists(): with open(env_file, 'r') as f: content = f.read() secret_match = re.search(r'SECRET_KEY=(.+)', content) if secret_match: secret_key = secret_match.group(1).strip() if len(secret_key) < 32: issues_found.append(f"SECRET_KEY is too short ({len(secret_key)} chars, need 64+)") print(f" ⚠ SECRET_KEY is weak (length: {len(secret_key)})") print(f" → Generate new key: python3 -c \"import secrets; print(secrets.token_hex(32))\"") else: print(" ✓ SECRET_KEY length is adequate") else: issues_found.append("SECRET_KEY not found in .env") print(" ✗ SECRET_KEY not found") # Check 3: Database password strength print("\n🗄️ Checking database password...") if env_file.exists(): with open(env_file, 'r') as f: content = f.read() uri_match = re.search(r'POSTGRESQL_URI=(.+)', content) if uri_match: uri = uri_match.group(1).strip() is_valid, msg = validate_postgresql_uri(uri) if not is_valid: issues_found.append(f"Database URI: {msg}") print(f" ✗ {msg}") else: print(" ✓ Database URI appears secure") else: issues_found.append("POSTGRESQL_URI not found") print(" ✗ POSTGRESQL_URI not configured") # Check 4: .gitignore exists print("\n📝 Checking .gitignore...") gitignore = project_root / ".gitignore" if gitignore.exists(): with open(gitignore, 'r') as f: content = f.read() if '*.env' in content or '.env' in content: print(" ✓ .gitignore protects .env files") else: issues_found.append(".env files not in .gitignore") print(" ✗ .env files not protected by .gitignore") else: issues_found.append(".gitignore does not exist") print(" ✗ .gitignore not found") # Summary print("\n" + "="*64) print("SUMMARY") print("="*64) if issues_found: print(f"\n⚠️ {len(issues_found)} security issue(s) found:") for i, issue in enumerate(issues_found, 1): print(f" {i}. {issue}") else: print("\n✅ No critical security issues found") if fixes_applied: print(f"\n✓ {len(fixes_applied)} fix(es) applied:") for fix in fixes_applied: print(f" • {fix}") print("\n📋 NEXT STEPS:") print(" 1. Rotate SECRET_KEY immediately if weak") print(" 2. Update database password if using defaults") print(" 3. Never commit .env files to git") print(" 4. Review all environment variables") print(" 5. Run this script regularly") return 0 if not issues_found else 1 if __name__ == "__main__": sys.exit(main())