Security: fix middleware order and CSRF protection

This commit is contained in:
2026-01-07 23:14:51 +03:00
parent f5130a79fd
commit b7fffb55bf

View File

@@ -18,7 +18,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# Initialize environment variables # Initialize environment variables
env = environ.Env( env = environ.Env(
# Set casting and default values # Set casting and default values
DEBUG=(bool, True), DEBUG=(bool, False), # Security: default False
SECRET_KEY=(str, 'django-insecure-default-key-change-in-production'), SECRET_KEY=(str, 'django-insecure-default-key-change-in-production'),
) )
@@ -36,16 +36,19 @@ if env_file.exists():
SECRET_KEY = env('SECRET_KEY') SECRET_KEY = env('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG') DEBUG = env.bool('DEBUG', False)
DEBUG_TOOLBAR_ENABLED = DEBUG and env.bool('DEBUG_TOOLBAR_ENABLED', False)
# Allowed hosts: читаем из переменной окружения или используем wildcard для разработки # Allowed hosts: читаем из переменной окружения
# В .env на проде: ALLOWED_HOSTS=mix.smaa.by,*.mix.smaa.by
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*']) ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*'])
# CSRF trusted origins для работы за nginx proxy # CSRF configuration
CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=[ CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=[
'https://mix.smaa.by', 'https://mix.smaa.by',
'https://*.mix.smaa.by', 'https://*.mix.smaa.by',
]) ])
CSRF_USE_SESSIONS = True # Рекомендуется для мультихостовых систем
# ============================================ # ============================================
@@ -93,8 +96,8 @@ TENANT_APPS = [
# Объединяем для INSTALLED_APPS # Объединяем для INSTALLED_APPS
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS] INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]
# Django Debug Toolbar (только в DEBUG режиме) # Django Debug Toolbar
if DEBUG: if DEBUG_TOOLBAR_ENABLED:
INSTALLED_APPS += ['debug_toolbar'] INSTALLED_APPS += ['debug_toolbar']
# Модели тенанта и домена # Модели тенанта и домена
@@ -110,22 +113,21 @@ SHOW_PUBLIC_IF_NO_TENANT_FOUND = True
# ============================================ # ============================================
MIDDLEWARE = [ MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware', # Static files first (no DB access needed) 'django_tenants.middleware.main.TenantMainMiddleware', # 1. Обязательно первым!
'django_tenants.middleware.main.TenantMainMiddleware', # ОБЯЗАТЕЛЬНО ПЕРВЫМ! 'django.middleware.security.SecurityMiddleware', # 2. Безопасность (HTTPS и заголовки)
'myproject.admin_access_middleware.TenantAdminAccessMiddleware', # SECURITY: Ограничение доступа к админке 'whitenoise.middleware.WhiteNoiseMiddleware', # 3. Статика (после безопасности)
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # 4. Определение пользователя
'myproject.admin_access_middleware.TenantAdminAccessMiddleware', # 5. ТЕПЕРЬ проверка работает (после auth)
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'simple_history.middleware.HistoryRequestMiddleware', # История изменений 'simple_history.middleware.HistoryRequestMiddleware',
] ]
# Django Debug Toolbar Middleware (только в DEBUG режиме) # Django Debug Toolbar Middleware
# ВАЖНО: добавляем ПОСЛЕ TenantMainMiddleware, чтобы toolbar видел корректный tenant if DEBUG_TOOLBAR_ENABLED:
if DEBUG:
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware') MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
@@ -433,23 +435,36 @@ USE_HTTPS = env.bool('USE_HTTPS', default=False)
# DJANGO DEBUG TOOLBAR SETTINGS # DJANGO DEBUG TOOLBAR SETTINGS
# ============================================ # ============================================
if DEBUG: # Улучшенный блок INTERNAL_IPS (для Docker и локальной разработки)
# IP адреса, с которых разрешен доступ к Debug Toolbar if DEBUG_TOOLBAR_ENABLED:
INTERNAL_IPS = [ import socket
'127.0.0.1', INTERNAL_IPS = ['127.0.0.1', '10.0.2.2', 'localhost']
'localhost',
]
# Если запускаете в Docker, добавьте IP хоста
# Например: INTERNAL_IPS += ['172.17.0.1', '192.168.65.1'] # Docker Desktop
try:
# Получаем все IP хоста (контейнера)
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
for ip_str in ips:
try:
# Для IPv4: заменяем последний октет на 1 (шлюз Docker)
if '.' in ip_str:
parts = ip_str.split('.')
if len(parts) == 4:
gateway_ip = '.'.join(parts[:3] + ['1'])
INTERNAL_IPS.append(gateway_ip)
# Возможные шлюзы и алиасы
INTERNAL_IPS.extend([
'.'.join(parts[:3] + ['254']),
'host.docker.internal',
])
except (ValueError, AttributeError):
continue
except Exception as e:
print(f"Debug Info: Could not determine Docker gateway IPs: {e}")
# Конфигурация Debug Toolbar # Конфигурация Debug Toolbar
DEBUG_TOOLBAR_CONFIG = { DEBUG_TOOLBAR_CONFIG = {
# Показывать toolbar всегда при DEBUG=True и INTERNAL_IPS 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG_TOOLBAR_ENABLED,
'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG,
# Или можно фильтровать по tenant (например, только для определенных поддоменов):
# 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG and getattr(request, 'tenant', None) and request.tenant.schema_name != 'public',
# Отключить для тестов
'IS_RUNNING_TESTS': False, 'IS_RUNNING_TESTS': False,
} }