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

发布于 2024-12-15 23:11:42 字数 1935 浏览 2 评论 0原文

我最初的问题是如何为 Django 登录页面启用 HTTPS< /a>,也是唯一的响应,建议我 - 将整个网站设为仅 HTTPS

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

一个回复提到了中间件解决方案,但有一个警告:

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

有关服务器故障的问题 nginx 重写为 https,还提到了 POST 丢失数据的问题,而且我对 nginx 不够熟悉,无法确定该解决方案的效果如何。

EFF 建议仅使用 HTTPS,并指出:

当以下情况发生时,应用程序必须在 cookie 上设置安全属性: 设置它。该属性指示浏览器发送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 会出现可见的闪烁。每次搜索都会发生这种情况,即使是相同的搜索词(我猜 https 确实不会缓存:) 更糟糕的是,当我在 IE 中测试它时,我首先收到消息:

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

单击“是”后,立即显示:

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

尽管第二个 IE 警告有一个将其关闭的选项,但第一个警告却没有,因此每次有人进行搜索并重定向到结果页面时,他们都会收到至少一条警告消息。

My original question was how to enable HTTPS for a Django login page, and the only response, recommended that I - make the entire site as HTTPS-only.

Given that I'm using Django 1.3 and nginx, what's the correct way to make a site HTTPS-only?

The one response mentioned a middleware solution, but had the caveat:

Django can't perform a SSL redirect while maintaining POST data.
Please structure your views so that redirects only occur during GETs.

A question on Server Fault about nginx rewriting to https, also mentioned problems with POSTs losing data, and I'm not familiar enough with nginx to determine how well the solution works.

And EFF's recommendation to go HTTPS-only, notes that:

The application must set the Secure attribute on the cookie when
setting it. This attribute instructs the browser to send the cookie
only over secure (HTTPS) transport, never insecure (HTTP).

Do apps like Django-auth have the ability to set cookies as Secure? Or do I have to write more middleware?

So, what is the best way to configure the combination of Django/nginx to implement HTTPS-only, in terms of:

  • security
  • preservation of POST data
  • cookies handled properly
  • interaction with other Django apps (such as Django-auth), works properly
  • any other issues I'm not aware of :)

Edit - another issue I just discovered, while testing multiple browsers. Say I have the URL https://mysite.com/search/, which has a search form/button. I click the button, process the form in Django as usual, and do a Django HttpResponseRedirect to http://mysite.com/search?results="foo". Nginx redirects that to https://mysite.com/search?results="foo", as desired.

However - Opera has a visible flash when the redirection happens. And it happens every search, even for the same search term (I guess https really doesn't cache :) Worse, when I test it in IE, I first get the message:

You are about to be redirected to a connection that is not secure - continue?

After clicking "yes", this is immediately followed by:

You are about to view pages over a secure connection - continue?

Although the second IE warning has an option to turn it off - the first warning does not, so every time someone does a search and gets redirected to a results page, they get at least one warning message.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

渔村楼浪 2024-12-22 23:11:42

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

For the 2nd part of John C's answer, and Django 1.4+...

Instead of extending HttpResponseRedirect, you can change the request.scheme to https.
Because Django is behind Nginx's reverse proxy, it doesn't know the original request was secure.

In your Django settings, set the SECURE_PROXY_SSL_HEADER setting:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

Then, you need Nginx to set the custom header in the reverse proxy. In the Nginx site settings:

location / {
    # ... 
    proxy_set_header X-Forwarded-Proto $scheme;
}

This way request.scheme == 'https' and request.is_secure() returns True.
request.build_absolute_uri() returns https://... and so on...

反差帅 2024-12-22 23:11:42

这是我迄今为止制定的解决方案。分为两部分,配置nginx和为Django编写代码。 nginx 部分处理外部请求,将http页面重定向到https,Django代码处理内部 URL生成,有一个 http 前缀。 (至少,那些由 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 分支中姜戈。
SESSION_EXPIRE_AT_BROWSER_CLOSE = 正确的

第二部分 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 的代码,但这取决于你 - 这当然是一种选择。

Here is the solution I've worked out so far. There are two parts, configuring nginx, and writing code for Django. The nginx part handles external requests, redirecting http pages to https, and the Django code handles internal URL generation that has an http prefix. (At least, those resulting from a HttpResponseRedirect()). Combined, it seems to work well - as far as I can tell, the client browser never sees an http page that the users didn't type in themselves.

Part one, nginx configuration

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

Part two A, various secure cookie settings, from settings.py

SERVER_TYPE = "DEV"
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True # currently only in Dev branch of Django.
SESSION_EXPIRE_AT_BROWSER_CLOSE = True

Part two B, Django code

# 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

Note that it is possible to re-write HTTPS_Response() as a decorator. The advantage would be - not having to go through all your code and replace HttpResponseRedirect(). The disadvantage - you'd have to put the decorator in front of HttpResponseRedirect(), which is in Django at django.http.__init__.py. I didn't want to modify Django's code, but that's up to you - it's certainly one option.

纸短情长 2024-12-22 23:11:42

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

if you stick your entire site behind https, you don't need to worry about it on the django end. (assuming you don't need to protect your data between nginx and django, only between users and your server)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文