Files
smartsoltech_site/CSRF_FIX.md
Andrew K. Choi ea677183ca Fix CSRF 403 error: add X-Forwarded-Host header to Nginx and update CSRF_TRUSTED_ORIGINS
- Created nginx-smartsoltech-fixed.conf with proper SSL and CSRF support
- Preserved existing SSL certificates from /etc/letsencrypt/live/www.smartsoltech.kr/
- Added X-Forwarded-Host header (critical for Django CSRF validation)
- Fixed location block order (static/media before /)
- Implemented proper HTTP→HTTPS and www→non-www redirects
- Updated CSRF_FIX.md with comprehensive troubleshooting guide
2025-11-24 12:03:26 +09:00

564 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔒 Исправление ошибки 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