Django + Caddy = проблемы с защитой CSRF

Я развернул приложение Django 4 с Daphne (ASGI) в контейнере docker. Я использую Caddy в качестве обратного прокси перед ним. Все работает, но я не могу заполнить ни одну форму, потому что срабатывает защита CSRF. Так что, например, вход в систему администратора невозможен.

В настоящее время я могу получить доступ к интерфейсу администратора двумя способами:

  1. Непосредственно через docker, через SSH туннелированный порт
  2. Через 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.

В заключение:

  1. Caddy пересылает http-запрос к django незашифрованным (таким образом, HTTP-non-S между caddy и django).
  2. Django считает этот запрос небезопасным
  3. .
  4. Защита CSRF ожидает, что заголовок Origin, отправленный браузером, будет http://, потому что запрос небезопасен. В моем случае это https://, потому что мой браузер общается с Caddy по протоколу https
  5. .
  6. Потому что заголовок 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
Вернуться на верх