This commit is contained in:
Iliyan Angelov
2025-11-26 22:32:20 +02:00
commit ed94dd22dd
150 changed files with 14058 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
"""
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))