Как развернуть сайт только для HTTPS, с Django/nginx?
Мой первоначальный вопрос заключался в том, как включить HTTPS для страницы входа в Django, и единственный ответ, рекомендованный я - сделать весь сайт только HTTPS.
Учитывая, что я использую Django 1.3 и nginx, какой правильный способ сделать сайт HTTPS-only?
Один ответ упомянул промежуточное решение, но имел оговорку:
Django не может выполнять перенаправление SSL при сохранении данных POST. Пожалуйста, структурируйте свои представления, чтобы перенаправления возникали только во время GET.
Вопрос о сбое сервера о nginx переписывании в https, также упоминается проблемы с потерями данных POST, и я недостаточно знаком с nginx, чтобы определить, насколько хорошо решение работает.
И Рекомендация EFF для HTTPS-only, отмечает, что:
Приложение должно установить атрибут Secure в cookie, когда установив его. Этот атрибут указывает браузеру отправить файл cookie только через безопасный (HTTPS) транспорт, никогда небезопасный (HTTP).
У приложений, таких как Django-auth, есть возможность устанавливать cookie как Secure? Или мне нужно написать больше промежуточного программного обеспечения?
Итак, что лучше всего настроить сочетание Django/nginx для реализации HTTPS-only, в терминах:
- Безопасность
- сохранение данных POST
- cookies обработаны надлежащим образом
- взаимодействие с другими приложениями Django (например, Django-auth) работает нормально
- любые другие проблемы, о которых я не знаю:)
Изменить - еще одна проблема, которую я только что обнаружил, при тестировании нескольких браузеров. Скажем, у меня есть URL https://mysite.com/search/
, который имеет форму поиска/кнопку. Я нажимаю кнопку, обрабатываю форму в Django, как обычно, и делаю Django HttpResponseRedirect до http://mysite.com/search?results="foo"
. Nginx перенаправляет его на https://mysite.com/search?results="foo"
, если требуется.
Тем не менее - Opera имеет видимую вспышку, когда происходит перенаправление. И это происходит каждый поиск, даже для одного и того же поискового запроса (думаю, https действительно не кэширует:) Хуже, когда я тестирую его в IE, я сначала получаю сообщение:
Вы собираетесь перенаправить на небезопасное соединение - продолжить?
После нажатия "да", за этим сразу следует:
Вы собираетесь просматривать страницы через безопасное соединение - продолжить?
Хотя второе предупреждение IE имеет возможность отключить его - первое предупреждение не происходит, поэтому каждый раз, когда кто-то выполняет поиск и перенаправляется на страницу результатов, у них появляется хотя бы одно предупреждающее сообщение.
Ответы
Ответ 1
Для второй части ответа Джона C и Django 1.4 +...
Вместо расширения HttpResponseRedirect вы можете изменить request.scheme
на https
.
Поскольку Django находится за обратным прокси-сервером Nginx, он не знает, что исходный запрос был безопасным.
В настройках Django установите параметр SECURE_PROXY_SSL_HEADER:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Затем вам понадобится Nginx для установки настраиваемого заголовка в обратном прокси. В настройках сайта Nginx:
location / {
# ...
proxy_set_header X-Forwarded-Proto $scheme;
}
Таким образом request.scheme == 'https'
и request.is_secure()
возвращает значение True.
request.build_absolute_uri()
возвращает https://...
и так далее...
Ответ 2
Вот решение, которое я разработал до сих пор. Есть две части, настройка nginx и написание кода для Django. Часть nginx обрабатывает внешние запросы, перенаправляя http
страницы на https
, а код Django обрабатывает внутреннюю структуру URL, которая имеет префикс http
. (По крайней мере, те, которые возникают в результате a HttpResponseRedirect()
). Комбинированный, кажется, работает хорошо - насколько я могу судить, клиентский браузер никогда не видит страницу http
, которую пользователи не вводили сами.
Часть первая, конфигурация nginx
# nginx.conf
# Redirects any requests on port 80 (http) to https:
server {
listen 80;
server_name www.mysite.com mysite.com;
rewrite ^ https://mysite.com$request_uri? permanent;
# rewrite ^ https://mysite.com$uri permanent; # also works
}
# django pass-thru via uWSGI, only from https requests:
server {
listen 443;
ssl on;
ssl_certificate /etc/ssl/certs/mysite.com.chain.crt;
ssl_certificate_key /etc/ssl/private/mysite.com.key;
server_name mysite.com;
location / {
uwsgi_pass 127.0.0.1:8088;
include uwsgi_params;
}
}
Часть вторая A, различные настройки безопасного файла cookie, из settings.py
SERVER_TYPE = "DEV"
SESSION_COOKIE_HTTPONLY= True
SESSION_COOKIE_SECURE= True
CSRF_COOKIE_SECURE= True # в настоящее время только в ветке Dev Django.
SESSION_EXPIRE_AT_BROWSER_CLOSE= True
Часть вторая B, код Django
# mysite.utilities.decorators.py
import settings
def HTTPS_Response(request, URL):
if settings.SERVER_TYPE == "DEV":
new_URL = URL
else:
absolute_URL = request.build_absolute_uri(URL)
new_URL = "https%s" % absolute_URL[4:]
return HttpResponseRedirect(new_URL)
# views.py
def show_items(request):
if request.method == 'POST':
newURL = handle_post(request)
return HTTPS_Response(request, newURL) # replaces HttpResponseRedirect()
else: # request.method == 'GET'
theForm = handle_get(request)
csrfContext = RequestContext(request, {'theForm': theForm,})
return render_to_response('item-search.html', csrfContext)
def handle_post(request):
URL = reverse('item-found') # name of view in urls.py
item = request.REQUEST.get('item')
full_URL = '%s?item=%s' % (URL, item)
return full_URL
Обратите внимание, что можно переписать HTTPS_Response()
в качестве декоратора. Преимуществом будет то, что вам не придется проходить через весь ваш код и заменить HttpResponseRedirect()
. Недостаток - вам нужно поставить декоратор перед HttpResponseRedirect()
, который находится в Django в django.http.__init__.py
. Я не хотел изменять код Django, но это зависит от вас - это, безусловно, один из вариантов.
Ответ 3
если вы поместите весь свой сайт за https, вам не нужно беспокоиться об этом на конце django. (при условии, что вам не нужно защищать свои данные между nginx и django, только между пользователями и вашим сервером)