Django + Caddy = проблемы с защитой CSRF
Я развернул приложение Django 4 с Daphne (ASGI) в контейнере docker. Я использую Caddy в качестве обратного прокси перед ним. Все работает, но я не могу заполнить ни одну форму, потому что срабатывает защита CSRF. Так что, например, вход в систему администратора невозможен.
В настоящее время я могу получить доступ к интерфейсу администратора двумя способами:
- Непосредственно через docker, через SSH туннелированный порт
- Через Caddy, который затем перенаправляется в контейнер Docker.
Вариант 1 работает. Я могу войти в интерфейс администратора так же, как если бы я запускал сервер разработки локально. Все работает, как ожидалось.
Однако вариант 2 (обратный прокси-сервер caddy) не работает. Я могу получить доступ к Django и загрузить страницы, но отправка любой формы будет заблокирована, потому что срабатывает защита CSRF.
CSRF verification failed. Request aborted.
Reason given for failure:
Origin checking failed - https://<mydomain.com> does not match any trusted origins.
Мой Caddyfile содержит следующее:
<mydomain.com> {
reverse_proxy localhost:8088
}
localhost:8088 - это порт, открытый моим контейнером docker.
В попытке устранить потенциальные проблемы, я установил следующее значение false в моем конфигурационном файле:
SECURE_SSL_REDIRECT
(вызывает цикл перенаправления, возможно, связанный с обратным проксированием)SESSION_COOKIE_SECURE
(я бы предпочел, чтобы это было установлено в True, но сейчас я не знаю)CSRF_COOKIE_SECURE
(то же замечание)
Единственные примеры Django-Caddy, которые я смог найти в сети, устарели и относятся к старым версиям Caddy и/или Django. Django развернут на ASGI с помощью Daphne.
Я видел сообщения, предлагающие изменить CSRF_TRUSTED_ORIGINS
, но этот вариант описан как A list of trusted origins for unsafe requests (e.g. POST).
. Небезопасно? Это не похоже на правильное решение. Это также не объясняет, почему он работает непосредственно на контейнере docker, если только localhost не является особым случаем для CSRF.
Версии:
- Caddy: 2.5.1
- Django: 4.0.5
- Daphne: 3.0.2
- Python: 3.10.5
Есть идеи, что идет не так, и как я должен отлаживать такие проблемы?
Наконец-то выяснил, что происходит.
Сначала я хотел узнать точный HTTP запрос, который был отправлен от caddy к django:
sudo tcpdump -i lo -A -n port 8088
Это подтверждает, что:
- заголовки
Origin
иReferer
были установлены правильно - куки
csrftoken
были отправлены правильно
Когда это стало известно, я смог покопаться в коде из django. В частности, эта функция в CSRF middleware.
В заключение:
- Caddy пересылает http-запрос к django незашифрованным (таким образом, HTTP-non-S между caddy и django).
- Django считает этот запрос небезопасным .
- Защита CSRF ожидает, что заголовок
Origin
, отправленный браузером, будетhttp://
, потому что запрос небезопасен. В моем случае этоhttps://
, потому что мой браузер общается с Caddy по протоколу https .
- Потому что заголовок
Origin
не соответствует тому, что ожидает промежуточное ПО CSRF, запрос отклоняется
На самом деле это простое исправление.
Поскольку мы знаем, что Caddy всегда будет игнорировать X-Forwarded-Proto
от браузера и устанавливает его сам, мы можем добавить SECURE_PROXY_SSL_HEADER к settings.py
в django:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
И вуаля!
Теперь я также могу установить их в true:
- SECURE_SSL_REDIRECT
- SESSION_COOKIE_SECURE
- CSRF_COOKIE_SECURE