diff --git a/CSRF_FIX.md b/CSRF_FIX.md new file mode 100644 index 0000000..03accf8 --- /dev/null +++ b/CSRF_FIX.md @@ -0,0 +1,563 @@ +# 🔒 Исправление ошибки CSRF 403 в админке через Nginx + +## ❌ Проблема +``` +Ошибка доступа (403) +Ошибка проверки CSRF. Запрос отклонён. +``` + +Появляется при входе в админку через Nginx (https://smartsoltech.kr/admin/) + +## 💡 Причина + +Django проверяет источник запроса (origin) для защиты от CSRF атак. Когда запрос идёт через Nginx: +1. Django видит заголовок `Origin: https://smartsoltech.kr` +2. Но в `CSRF_TRUSTED_ORIGINS` нет этого домена +3. Nginx не передаёт заголовок `X-Forwarded-Host` (критично для CSRF) +4. Django отклоняет запрос как небезопасный + +**Ваша конфигурация SSL:** Сертификаты находятся в `/etc/letsencrypt/live/www.smartsoltech.kr/` + +--- + +## ✅ Решение (2 простых шага) + +### Шаг 1: Обновить Nginx конфигурацию + +```bash +# На сервере +cd /opt/smartsoltech_site +git pull origin master + +# Применить исправленную конфигурацию (сохраняет ваши SSL сертификаты) +sudo cp nginx-smartsoltech-fixed.conf /etc/nginx/sites-available/smartsoltech + +# Проверить синтаксис +sudo nginx -t + +# Если OK - перезагрузить Nginx +sudo systemctl reload nginx +``` + +**Что исправили в Nginx:** +- ✅ Добавлен `X-Forwarded-Host: $host` (критично для CSRF) +- ✅ Сохранены ваши SSL сертификаты из `/etc/letsencrypt/live/www.smartsoltech.kr/` +- ✅ Правильный порядок location блоков (/static/ перед /) +- ✅ Redirect www → non-www +- ✅ Redirect HTTP → HTTPS + +### Шаг 2: Обновить CSRF_TRUSTED_ORIGINS в .env + +```bash +# Редактировать .env +cd /opt/smartsoltech_site +nano .env +``` + +**НАЙТИ строку:** +```env +CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr +``` + +**ЗАМЕНИТЬ НА:** +```env +CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr +``` + +**Сохранить:** Ctrl+O, Enter, Ctrl+X + +**Перезапустить Django:** +```bash +docker restart django_app + +# Проверить что контейнер запущен +docker ps | grep django_app +``` + +--- + +### Вариант 2: Ручное исправление + +#### Шаг 1: Исправить Nginx конфиг + +```bash +sudo nano /etc/nginx/sites-available/smartsoltech +``` + +**Найти секцию location / и добавить заголовки:** + +```nginx + location / { + proxy_pass http://localhost:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; # ДОБАВИТЬ + proxy_set_header X-Forwarded-Port $server_port; # ДОБАВИТЬ + proxy_redirect off; + proxy_buffering off; + ... + } +``` + +**Проверить и перезагрузить:** +```bash +sudo nginx -t +sudo systemctl reload nginx +``` + +#### Шаг 2: Исправить .env файл + +```bash +cd /opt/smartsoltech_site +nano .env +``` + +**Найти строку:** +```env +CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr +``` + +**Заменить на:** +```env +CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr +``` + +**Сохранить:** Ctrl+O, Enter, Ctrl+X + +#### Шаг 3: Перезапустить Django + +```bash +docker compose restart django_app + +# Проверить логи +docker logs django_app --tail 50 +``` + +--- + +## 🧪 Проверка работы + +### Тест 1: Проверка переменных окружения + +```bash +# Убедиться что Django видит правильный CSRF_TRUSTED_ORIGINS +docker exec django_app env | grep CSRF_TRUSTED_ORIGINS + +# Должен вывести: +# CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr +``` + +### Тест 2: Проверка входа в админку + +1. Открыть браузер: **https://smartsoltech.kr/admin/** +2. Ввести логин и пароль +3. Нажать "Войти" +4. ✅ **Должен успешно войти без ошибки 403** + +### Тест 3: Проверка Nginx заголовков + +```bash +# Проверить что Nginx передаёт все нужные заголовки +curl -v https://smartsoltech.kr/admin/ 2>&1 | grep -i forward + +# Должны быть: +# X-Forwarded-Proto: https +# X-Forwarded-Host: smartsoltech.kr +``` + +--- + +## 🔧 Если проблема осталась + +### Диагностика 1: Проверить Django логи + +```bash +# Смотреть логи в реальном времени +docker logs -f django_app + +# Искать строки с CSRF: +# Forbidden (CSRF cookie not set.) +# Forbidden (CSRF token missing or incorrect) +# Origin checking failed - https://smartsoltech.kr does not match any trusted origins +``` + +### Диагностика 2: Проверить Nginx конфиг + +```bash +# Проверить что изменения применились +sudo nginx -T | grep -A 10 "location /" + +# Должны быть все proxy_set_header, особенно: +# proxy_set_header X-Forwarded-Host $host; +``` + +### Диагностика 3: Очистить кэш браузера + +1. Открыть DevTools (F12) +2. Перейти в Network +3. Правый клик → "Clear browser cache" +4. Попробовать войти заново + +### Диагностика 4: Временно включить DEBUG (ОСТОРОЖНО!) + +```bash +# ТОЛЬКО ДЛЯ ДИАГНОСТИКИ! Выключить сразу после! +nano /opt/smartsoltech_site/.env + +# Изменить +DEBUG=True + +# Перезапустить +docker restart django_app + +# Открыть админку - увидите детальную ошибку + +# СРАЗУ ВЕРНУТЬ: +DEBUG=False +docker restart django_app +``` + +--- + +## 📝 Объяснение что было исправлено + +### Проблема в деталях: + +1. **Браузер** → отправляет HTTPS запрос: `https://smartsoltech.kr/admin/login/` +2. **Nginx** → принимает HTTPS, проксирует к Django как HTTP: `http://localhost:8000` +3. **Django** → проверяет CSRF токен +4. **Django** → сравнивает Origin с CSRF_TRUSTED_ORIGINS +5. **❌ Ошибка:** В CSRF_TRUSTED_ORIGINS не было `https://smartsoltech.kr` +6. **❌ Ошибка:** Nginx не передавал `X-Forwarded-Host` → Django не знал реальный домен +7. **Результат:** Django отклонял запрос (403 Forbidden) + +### Что исправили: + +1. ✅ Добавили `https://smartsoltech.kr` в CSRF_TRUSTED_ORIGINS +2. ✅ Добавили `http://smartsoltech.kr` для HTTP→HTTPS redirect +3. ✅ Nginx передаёт `X-Forwarded-Proto: https` +4. ✅ Nginx передаёт `X-Forwarded-Host: smartsoltech.kr` (КРИТИЧНО!) +5. ✅ Django понимает что оригинальный запрос был HTTPS от smartsoltech.kr +6. ✅ CSRF проверка проходит успешно + +--- + +## 🔐 Правильная финальная конфигурация + +### 📄 .env (на сервере): +```env +DEBUG=False +ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr,192.168.0.102 +CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr +``` + +### 📄 Nginx /etc/nginx/sites-available/smartsoltech: + +```nginx +upstream django_app { + server localhost:8000; +} + +# HTTP → HTTPS redirect +server { + listen 80; + server_name smartsoltech.kr www.smartsoltech.kr; + return 301 https://$host$request_uri; +} + +# www → non-www redirect +server { + listen 443 ssl http2; + server_name www.smartsoltech.kr; + + ssl_certificate /etc/letsencrypt/live/www.smartsoltech.kr/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.smartsoltech.kr/privkey.pem; + + return 301 https://smartsoltech.kr$request_uri; +} + +# Main HTTPS server +server { + listen 443 ssl http2; + server_name smartsoltech.kr; + + # SSL certificates (ваши существующие сертификаты) + ssl_certificate /etc/letsencrypt/live/www.smartsoltech.kr/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.smartsoltech.kr/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + # Security headers + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + + # Static files (ПЕРЕД location /) + location /static/ { + alias /opt/smartsoltech_site/smartsoltech/staticfiles/; + expires 30d; + add_header Cache-Control "public, immutable"; + } + + location /media/ { + alias /opt/smartsoltech_site/smartsoltech/media/; + expires 7d; + } + + # Django proxy (с CSRF заголовками) + location / { + proxy_pass http://django_app; + + # КРИТИЧНО для CSRF: + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; # ← ВАЖНО! + + proxy_redirect off; + proxy_buffering off; + proxy_read_timeout 60s; + proxy_connect_timeout 60s; + } +} +``` + +--- + +## ⚡ Быстрое решение одной командой + +```bash +# НА СЕРВЕРЕ (выполнить всё за раз): +cd /opt/smartsoltech_site && \ +git pull origin master && \ +sudo cp nginx-smartsoltech-fixed.conf /etc/nginx/sites-available/smartsoltech && \ +sudo nginx -t && sudo systemctl reload nginx && \ +sed -i 's|CSRF_TRUSTED_ORIGINS=.*|CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr|' .env && \ +docker restart django_app && \ +sleep 3 && \ +echo "✅ CSRF исправлен! Проверьте: https://smartsoltech.kr/admin/" && \ +docker ps | grep django_app +``` + +--- + +## ✅ Финальный чеклист + +- [ ] Git pull выполнен (получен nginx-smartsoltech-fixed.conf) +- [ ] Nginx конфиг скопирован в /etc/nginx/sites-available/smartsoltech +- [ ] `sudo nginx -t` успешно (OK) +- [ ] `sudo systemctl reload nginx` выполнен +- [ ] CSRF_TRUSTED_ORIGINS обновлён в .env (добавлены http:// и https:// версии) +- [ ] `docker restart django_app` выполнен +- [ ] Контейнер django_app запущен (docker ps) +- [ ] **Вход в админку работает без ошибки 403** ✅ +- [ ] DEBUG=False в .env (безопасность) + +--- + +## 📞 Если нужна помощь + +Если после всех шагов проблема осталась, отправьте: + +```bash +# 1. Django логи +docker logs django_app --tail 100 + +# 2. Nginx конфиг +sudo nginx -T | grep -A 30 "server_name smartsoltech.kr" + +# 3. Переменные окружения Django +docker exec django_app env | grep -E "(CSRF|ALLOWED|DEBUG)" + +# 4. Тест curl +curl -v https://smartsoltech.kr/admin/ 2>&1 | head -50 +``` + +--- + +**Создано:** 24 ноября 2025 г. +**Проблема:** CSRF 403 ошибка при входе в админку через Nginx + SSL +**Решение:** Добавить домен в CSRF_TRUSTED_ORIGINS + исправить Nginx заголовки (X-Forwarded-Host) +**Статус:** ✅ Готово к применению + + +# НЕ должно быть: +# HTTP/1.1 403 Forbidden +``` + +### Проверка 2: Через браузер + +1. Открыть http://smartsoltech.kr/admin/ +2. Ввести логин и пароль +3. Нажать "Войти" +4. ✅ Должно успешно войти в админку +5. ❌ НЕ должно показывать ошибку CSRF 403 + +### Проверка 3: Логи Django + +```bash +docker logs django_app --tail 20 | grep -i csrf + +# НЕ должно быть: +# "CSRF verification failed" +# "Forbidden (CSRF token missing or incorrect)" +``` + +--- + +## 📝 Понимание настроек CSRF + +### CSRF_TRUSTED_ORIGINS - что это? + +Django сравнивает HTTP заголовок `Origin` с этим списком: + +```python +# В Django settings.py читается из .env: +CSRF_TRUSTED_ORIGINS = [ + 'http://localhost:8000', # Для локальной разработки + 'http://smartsoltech.kr', # Для HTTP запросов (до получения SSL) + 'https://smartsoltech.kr', # Для HTTPS запросов (после SSL) +] +``` + +### Когда нужен каждый origin: + +| Origin | Когда используется | +|--------|-------------------| +| `http://localhost:8000` | Django напрямую (без Nginx) | +| `http://smartsoltech.kr` | Через Nginx БЕЗ SSL | +| `https://smartsoltech.kr` | Через Nginx С SSL | + +### После получения SSL сертификата: + +```env +# Можно убрать HTTP, оставить только HTTPS: +CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr,https://www.smartsoltech.kr +``` + +--- + +## 🔧 Дополнительные проверки + +### Если ошибка всё ещё есть: + +#### 1. Проверить что Django видит правильные заголовки + +```bash +# Включить DEBUG=True временно +nano /opt/smartsoltech_site/.env +# DEBUG=True + +docker compose restart django_app + +# Открыть http://smartsoltech.kr/admin/ в браузере +# Django покажет детальную информацию об ошибке +``` + +#### 2. Проверить ALLOWED_HOSTS + +```bash +# В .env должно быть: +ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr +``` + +#### 3. Проверить что Nginx передаёт правильный Host + +```bash +# В логах Django должен быть правильный host +docker logs django_app --tail 50 | grep "GET /admin" + +# Должно показывать: +# [INFO] "GET /admin/ HTTP/1.1" 200 +# Host: smartsoltech.kr +``` + +#### 4. Тест с curl + +```bash +# Симуляция браузера с Origin заголовком +curl -H "Origin: http://smartsoltech.kr" \ + -H "Referer: http://smartsoltech.kr/admin/" \ + -I http://smartsoltech.kr/admin/login/ + +# Должно вернуть 200 или 302, НЕ 403 +``` + +--- + +## ⚠️ Важные замечания + +### Безопасность + +1. **DEBUG=False в продакшене** - всегда! + ```env + DEBUG=False # ✅ В продакшене + DEBUG=True # ❌ Только для отладки локально + ``` + +2. **HTTPS рекомендуется** - после получения SSL: + ```nginx + # Редирект всех HTTP на HTTPS + server { + listen 80; + return 301 https://$host$request_uri; + } + ``` + +3. **www.smartsoltech.kr** - если используется: + ```env + ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr + CSRF_TRUSTED_ORIGINS=http://smartsoltech.kr,http://www.smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr + ``` + +### После получения SSL + +1. Обновить Nginx конфиг (раскомментировать SSL строки) +2. Обновить .env: + ```env + # Можно убрать HTTP origins (оставить только для localhost) + CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr,https://www.smartsoltech.kr + ``` +3. Перезапустить: + ```bash + sudo systemctl reload nginx + docker compose restart django_app + ``` + +--- + +## 🎯 Быстрое решение (одной командой) + +```bash +# На сервере выполнить всё сразу: +cd /opt/smartsoltech_site && \ +git pull origin master && \ +sudo cp nginx-smartsoltech.conf /etc/nginx/sites-available/smartsoltech && \ +sudo nginx -t && \ +sudo systemctl reload nginx && \ +sed -i 's|CSRF_TRUSTED_ORIGINS=.*|CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr|' .env && \ +docker compose restart django_app && \ +echo "✅ CSRF исправлен! Проверьте: http://smartsoltech.kr/admin/" +``` + +--- + +## 📊 Чеклист исправления + +- [ ] Nginx конфиг обновлён (добавлены X-Forwarded-Host и X-Forwarded-Port) +- [ ] `sudo nginx -t` успешно +- [ ] `sudo systemctl reload nginx` выполнен +- [ ] `.env` обновлён (добавлен http://smartsoltech.kr в CSRF_TRUSTED_ORIGINS) +- [ ] Django перезапущен (`docker compose restart django_app`) +- [ ] Админка открывается без ошибки 403 +- [ ] Вход в админку работает +- [ ] Логи Django без ошибок CSRF + +--- + +**Создано:** 24 ноября 2025 г. +**Проблема:** CSRF 403 при входе в админку через Nginx +**Решение:** Добавить X-Forwarded заголовки в Nginx + добавить http://smartsoltech.kr в CSRF_TRUSTED_ORIGINS diff --git a/nginx-smartsoltech-fixed.conf b/nginx-smartsoltech-fixed.conf new file mode 100644 index 0000000..31d6be2 --- /dev/null +++ b/nginx-smartsoltech-fixed.conf @@ -0,0 +1,108 @@ +# SmartSolTech Nginx Configuration +# Исправленная версия с SSL сертификатами и CSRF headers + +upstream django_app { + server localhost:8000; +} + +# HTTP → HTTPS редирект +server { + listen 80; + listen [::]:80; + server_name smartsoltech.kr www.smartsoltech.kr; + + # Let's Encrypt challenge + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +# Редирект с www на non-www (HTTPS) +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name www.smartsoltech.kr; + + # SSL сертификаты + ssl_certificate /etc/letsencrypt/live/www.smartsoltech.kr/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.smartsoltech.kr/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + return 301 https://smartsoltech.kr$request_uri; +} + +# Основной HTTPS сервер +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name smartsoltech.kr; + + # SSL сертификаты + ssl_certificate /etc/letsencrypt/live/www.smartsoltech.kr/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.smartsoltech.kr/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + # Максимальный размер загружаемых файлов + client_max_body_size 100M; + + # Security headers + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Логи + access_log /var/log/nginx/smartsoltech_access.log; + error_log /var/log/nginx/smartsoltech_error.log; + + # Статические файлы - ВАЖНО: должны быть ПЕРЕД location / + location /static/ { + alias /opt/smartsoltech_site/smartsoltech/staticfiles/; + expires 30d; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Медиа файлы + location /media/ { + alias /opt/smartsoltech_site/smartsoltech/media/; + expires 7d; + add_header Cache-Control "public"; + access_log off; + } + + # Прокси к Django приложению + location / { + proxy_pass http://django_app; + + # Заголовки для Django + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # ВАЖНО для CSRF: Django должен знать оригинальный протокол + proxy_set_header X-Forwarded-Host $host; + + proxy_redirect off; + proxy_buffering off; + + # Таймауты + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # Deny access to sensitive files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } +}