【问题标题】:How to deploy an HTTPS-only site, with Django/nginx?如何使用 Django/nginx 部署仅限 HTTPS 的站点?
【发布时间】:2011-12-30 12:46:10
【问题描述】:

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

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

一个回复提到了a middleware solution,但有一个警告:

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

关于nginx rewriting to https的Server Fault上的一个问题,还提到了POST丢失数据的问题,我对nginx不够熟悉,无法确定解决方案的效果。

还有EFF's recommendation to go HTTPS-only,请注意:

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

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

那么,配置 Django/nginx 的组合以实现 HTTPS-only 的最佳方式是:

  • 安全
  • 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 警告有一个关闭它的选项 - 第一个 警告没有,所以每次有人进行搜索并被重定向到结果页面时,他们至少会收到一个警告消息。

【问题讨论】:

    标签: django https nginx django-authentication


    【解决方案1】:

    对于 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://... 等等...

    【讨论】:

    • django 的文档似乎建议 HTTP_X_FORWARDED_PROTO 而不是 HTTP_X_FORWARDED_PROTOCOL 是否重要?
    • 只要您在 nginx 和 Django 设置中设置相同的标头(nginx 中的 X-Forwarded-Proto),两者都可以工作(以及任何其他名称)。写这篇文章时,我不知道“proto”是惯例...
    • 我更新了答案以与文档保持一致,感谢您指出。
    • 适用于使用 nginx uwsgi服务django的任何人。请注意,您不需要指定“SECURE_PROXY_SSL_HEADER”,因为在这种情况下,django 已经知道请求是否安全。 is_secure() 正在检查scheme 属性,它调用_get_scheme(self),而这又应该由WSGIRequest 实现,它执行return self.environ.get('wsgi.url_scheme')。而且——你猜对了——这个环境变量由uwsgi相应地填充。
    • 如果您使用 AWS ALB/ELB 处理 http/https 重定向,并向您的 nginx 容器发送 http 请求,则将标头设置为 proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    【解决方案2】:

    这是我迄今为止制定的解决方案。分为两部分,配置 nginx 和为 Django 编写代码。 nginx 部分处理external 请求,将http 页面重定向到https,Django 代码处理具有http 前缀的internal URL 生成。 (至少,来自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 # 目前仅在 Django 的 Dev 分支中。
    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 之间保护你的数据,只需要在用户和你的服务器之间)

      【讨论】:

      • 你是说用nginx做所有的配置?
      • 您需要做的就是将 nginx 设置为仅响应 https 请求(可能将任何非 https 重定向到 https)。 django 可以通过 http 运行,因为它只在 localhost 上与 nginx 对话
      • 我将 nginx 配置为将所有内容重定向到 https,但它并没有完全起作用 - 重定向到带有变量的另一个 URL,如 https://mysite.com/search?results="abc",会丢弃变量。
      猜你喜欢
      • 2018-03-26
      • 2015-06-02
      • 1970-01-01
      • 2018-01-18
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多