""" Management command to check security settings and vulnerabilities. """ from django.core.management.base import BaseCommand from django.conf import settings from django.contrib.auth import get_user_model from accounts.models import FailedLoginAttempt, ActivityLog from django.utils import timezone from datetime import timedelta User = get_user_model() class Command(BaseCommand): help = 'Check security settings and report potential vulnerabilities' def handle(self, *args, **options): self.stdout.write(self.style.SUCCESS('=' * 60)) self.stdout.write(self.style.SUCCESS('Security Audit Report')) self.stdout.write(self.style.SUCCESS('=' * 60)) issues = [] warnings = [] # Check DEBUG mode if settings.DEBUG: warnings.append('DEBUG mode is enabled - disable in production!') else: self.stdout.write(self.style.SUCCESS('✓ DEBUG mode is disabled')) # Check SECRET_KEY if settings.SECRET_KEY == 'django-insecure-change-this-in-production': issues.append('CRITICAL: Default SECRET_KEY is being used!') else: self.stdout.write(self.style.SUCCESS('✓ SECRET_KEY is set')) # Check ALLOWED_HOSTS if not settings.ALLOWED_HOSTS: issues.append('ALLOWED_HOSTS is empty - set in production!') else: self.stdout.write(self.style.SUCCESS(f'✓ ALLOWED_HOSTS: {settings.ALLOWED_HOSTS}')) # Check HTTPS settings if not settings.DEBUG: if not getattr(settings, 'SECURE_SSL_REDIRECT', False): issues.append('SECURE_SSL_REDIRECT should be True in production') else: self.stdout.write(self.style.SUCCESS('✓ SSL redirect enabled')) # Check password hashers if 'argon2' in settings.PASSWORD_HASHERS[0].lower(): self.stdout.write(self.style.SUCCESS('✓ Using Argon2 password hasher')) else: warnings.append('Consider using Argon2 password hasher') # Check session security if settings.SESSION_COOKIE_HTTPONLY: self.stdout.write(self.style.SUCCESS('✓ Session cookies are HTTP-only')) else: issues.append('SESSION_COOKIE_HTTPONLY should be True') if settings.SESSION_COOKIE_SECURE or settings.DEBUG: self.stdout.write(self.style.SUCCESS('✓ Session cookies are secure')) else: issues.append('SESSION_COOKIE_SECURE should be True in production') # Check CSRF protection if settings.CSRF_COOKIE_HTTPONLY: self.stdout.write(self.style.SUCCESS('✓ CSRF cookies are HTTP-only')) else: issues.append('CSRF_COOKIE_HTTPONLY should be True') # Check failed login attempts recent_failures = FailedLoginAttempt.objects.filter( timestamp__gte=timezone.now() - timedelta(hours=24) ).count() if recent_failures > 0: self.stdout.write(self.style.WARNING(f'⚠ {recent_failures} failed login attempts in last 24 hours')) else: self.stdout.write(self.style.SUCCESS('✓ No recent failed login attempts')) # Check for users with weak passwords (if possible) users_without_mfa = User.objects.filter(mfa_enabled=False).count() total_users = User.objects.count() if total_users > 0: mfa_percentage = (users_without_mfa / total_users) * 100 if mfa_percentage > 50: warnings.append(f'Only {100-mfa_percentage:.1f}% of users have MFA enabled') else: self.stdout.write(self.style.SUCCESS(f'✓ {100-mfa_percentage:.1f}% of users have MFA enabled')) # Report issues if issues: self.stdout.write(self.style.ERROR('\n' + '=' * 60)) self.stdout.write(self.style.ERROR('CRITICAL ISSUES:')) for issue in issues: self.stdout.write(self.style.ERROR(f'✗ {issue}')) if warnings: self.stdout.write(self.style.WARNING('\n' + '=' * 60)) self.stdout.write(self.style.WARNING('WARNINGS:')) for warning in warnings: self.stdout.write(self.style.WARNING(f'⚠ {warning}')) if not issues and not warnings: self.stdout.write(self.style.SUCCESS('\n✓ No security issues found!')) self.stdout.write(self.style.SUCCESS('\n' + '=' * 60))