2014-12-17 1 views
2

Недавно я работал над приложением django и, наконец, готов к развертыванию в qa и производственной среде. Все работало отлично на месте, но, добавив сложность развертывания в реальном мире, у меня было несколько проблем.Ошибка Django CSRF, обнаруженная Nginx X-Forwarded-host

Первый мой технический стек немного сложнее. Для развертываний я использую aws для всего, где мой сайт развернут на нескольких ec2, поддерживаемых балансировщиком нагрузки. Балансировщик нагрузки защищен с помощью ssl, но соединения с балансировщиком нагрузки перенаправляются на ec2 по стандарту http на порт 80. После удара ec2 на порту 80 они отправляются в контейнер докера на порте 8000 (если вы не знакомы с докер просто считают его стандартным vm). Внутри контейнера nginx слушает порт 8000, он обрабатывает перенаправление для статических файлов в django, а для веб-запросов он отправляет запрос на django, работающий на 127.0.0.1:8001. Джанго в настоящее время размещается uwsgi прослушивает порт 8001.

server { 
    listen 8000; 
    server_name localhost; 

    location /static/ { 
     alias /home/library/deploy/thelibrary/static/; 
    } 

    location/{ 
     proxy_set_header X-Forwarded-Host $host:443; 
     proxy_pass http://127.0.0.1:8001/; 
    } 
} 

Я использую X-Forwarded хозяина, потому что у меня были проблемы с редиректов от Google OAuth и перенаправляет предложит пользователю войти в систему сделать запрос на брАузЕр URL 127,0. 0.1: 8001, который, очевидно, не будет работать. В моем файле settings.py я также включил

USE_X_FORWARDED_HOST = True 

Чтобы заставить django использовать правильный хост для переадресации.

В настоящее время общий просмотр сайта отлично работает, загрузка статических файлов, перенаправление работы, а сайт защищен с помощью ssl. Однако проблема заключается в том, что проверка CSRF не выполняется.

На форме представления я получаю следующее сообщение об ошибке

Referer проверки не удался - https://qa-load-balancer.com/projects/new не соответствует https://qa-load-balancer.com:443/.

Я действительно не уверен, что делать с этим, его действительно через вопросы о стеке, которые я получил до сих пор.

ответ

4

Вместо того, чтобы делать HTTP-прокси, я должен использовать Nginx's built-in capacity для связи с uWSGI. (Это все равно будет работать, если вы используете отдельные контейнеры Docker для Nginx и uWSGI, поскольку связь осуществляется через TCP)

Типичная конфигурация (шахты) выглядит следующим образом:

location/{ 
    uwsgi_pass http://127.0.0.1:8001; 
    include  uwsgi_params; 
} 

Вы должны будете удалить аргумент --http (или эквивалент конфигурационного файла) из вашего вызова uWSGI.

Кроме того, в uwsgi_params (найдено в /etc/nginx или в выбранном вами месте) существует несколько директив для передачи метаданных. Вот выдержка из шахты, которая выглядит, как это может быть связанно с вашей проблемой:

... 
uwsgi_param REQUEST_URI  $request_uri; 
uwsgi_param DOCUMENT_ROOT  $document_root; 
uwsgi_param SERVER_PROTOCOL $server_protocol; 
uwsgi_param HTTPS    $https if_not_empty; 

Соответствующих Документами http://uwsgi-docs.readthedocs.org/en/latest/WSGIquickstart.html#putting-behind-a-full-webserver

0

Для пользователей, которые не могут использовать встроенный объект Nginx, в вот первопричина:

  • Начиная с ~ Джаньо 1.9, проверка CSRF требует, чтобы Referer и Host матч, если не указать CSRF_TRUSTED_ORIGINS (см код вокруг REASON_BAD_REFERERhere)
  • Если вы не укажете CSRF_TRUSTED_ORIGINS, система возвращается на request.get_host()
  • request.get_host() использует request._get_raw_host()
  • request._get_raw_host() чеки последовательно HTTP_X_FORWARDED_HOST (если USE_X_FORWARDED_HOST установлен), HTTP_HOST и SERVER_NAME
  • Наиболее рекомендуемые конфигурации Nginx предполагают запись вроде proxy_set_header X-Forwarded-Host $host:$server_port;
  • В конце концов, реферер (напр. <host>) сравнивают с X-Forwarded-Host (например, <host>:<port>). Они не совпадают, поэтому CSRF терпит неудачу.

Об этом не так много, но билет Django #26037 ссылки RFC2616. Билет утверждает, что хозяин без порта «против спекуляции», но это не так, как спецификации на самом деле говорит:

А «хозяин» без какой-либо задней порта информации подразумевает порт по умолчанию для запрашиваемой услуги

Это приводит к (как минимум) следующие параметры (безопасный первый):

  • включают хост и порт в CSRF_TRUSTED_ORIGINS
  • удалить port из X-Forwarded-Host в конфигурации Nginx (в предположении, что не спекуляция X-Forwarded-Host следует ту же семантику, Host)

Чтобы избежать жесткого кодирования доменов в CSRF_TRUSTED_ORIGINS, второй вариант является привлекательным, но он может прийти с протестами безопасности. Спекулятивно:

  • X-Forwarded-Proto следует использовать для уточнения протокола (поскольку отсутствие порта предполагает протокол по умолчанию)
  • Обратный прокси-сервер должен использовать порт 443 для HTTPS (т.е. по умолчанию для протокола) и запретить не связанные с HTTPS (X-Forwarded-Proto могут исправить это).
Смежные вопросы