如何使用Django / nginx部署仅支持HTTPS的站点?

时间:2011-11-16 15:09:53

标签: django https nginx django-authentication

我最初的问题是如何enable HTTPS for a Django login page以及唯一的回复,建议我 - 将整个网站设为仅限HTTPS

鉴于我正在使用Django 1.3和nginx,那么使网站成为HTTPS的正确方法是什么?

一个回复提到了a middleware solution,但有一点需要注意:

  

Django在维护POST数据时无法执行SSL重定向。   请构建您的视图,以便重定向仅在GET期间发生。

有关nginx rewriting to https的服务器故障的问题,还提到POST丢失数据的问题,我对nginx不太熟悉,无法确定解决方案的工作情况。

EFF's recommendation to go HTTPS-only,注意到:

  

应用程序必须在cookie上设置Secure属性   设置它。此属性指示浏览器发送cookie   仅通过安全(HTTPS)传输,从不安全(HTTP)。

像Django-auth这样的应用程序是否能够将Cookie设置为安全?或者我是否必须编写更多中间件?

那么,根据以下几点,配置Django / nginx组合以实现仅HTTPS的最佳方法是什么:

  • 安全
  • 保存POST数据
  • 正确处理了Cookie
  • 与其他Django应用程序(例如Django-auth)的交互,正常工作
  • 我不知道的任何其他问题:)

编辑 - 我在测试多个浏览器时发现的另一个问题。假设我有一个URL https://mysite.com/search/,它有一个搜索表单/按钮。我点击按钮,像往常一样处理Django中的表单,并执行Django HttpResponseRedirecthttp://mysite.com/search?results="foo"。 Nginx根据需要将其重定向到https://mysite.com/search?results="foo"

但是 - 重定向发生时,Opera有一个可见的 flash 。它发生在每次搜索,即使是相同的搜索词(我猜https真的不缓存:)更糟糕的是,当我在IE中测试它时,我首先得到消息:

  

您将被重定向到不安全的连接 - 继续吗?

点击“是”后,紧接着是:

  

您即将通过安全连接查看网页 - 继续吗?

虽然第二个IE警告可以选择将其关闭 - 第一个警告没有,所以每当有人进行搜索并被重定向到结果页面时,他们至少会收到一个警告消息。

3 个答案:

答案 0 :(得分:51)

对于John C的回答的第二部分,以及Django 1.4 + ......

您可以将request.scheme更改为https,而不是扩展HttpResponseRedirect。 因为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://...,依此类推......

答案 1 :(得分:20)

这是迄今为止我已经解决的解决方案。有两个部分,配置nginx,以及为Django编写代码。 nginx部分处理外部请求,将http页面重定向到https,Django代码处理内部 URL生成{{1} } 字首。 (至少是由http产生的那些)。结合起来,似乎效果很好 - 据我所知,客户端浏览器永远不会看到用户自己没有键入的HttpResponseRedirect()页面。

第一部分,nginx配置

http

第二部分A,各种安全cookie设置,来自settings.py

SERVER_TYPE =“DEV”
SESSION_COOKIE_HTTPONLY =正确 SESSION_COOKIE_SECURE =正确 CSRF_COOKIE_SECURE = True#目前仅在Django的Dev分支中 SESSION_EXPIRE_AT_BROWSER_CLOSE =真

第二部分B,Django代码

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

请注意, 可以将# 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的代码,但这取决于你 - 这当然是一种选择。

答案 2 :(得分:5)

如果您将整个网站放在https后面,则无需担心django端。 (假设您不需要在nginx和django之间保护数据,仅在用户和服务器之间保护数据)