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
This commit is contained in:
2025-11-24 12:03:26 +09:00
parent a70ee08821
commit ea677183ca
2 changed files with 671 additions and 0 deletions

563
CSRF_FIX.md Normal file
View File

@@ -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

View File

@@ -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;
}
}