试图让基于 Django 的站点仅使用 HTTPS,但不确定它是否安全?

信息安全 tls 网址重定向 nginx
2021-09-06 08:40:47

EFF 建议在您的网站上到处使用 HTTPS,我相信该网站会同意。当我问一个关于在我的登录页面上使用 Django 实现 HTTPS 的问题时,这肯定是我得到的答复:)

所以我正试图做到这一点。我有一个 Django/nginx 设置,我正在尝试为仅 HTTPS 进行配置 - 它有点工作,但存在问题。更重要的是,尽管看到了https前缀,但我确定它是否真的很安全。

我已将 nginx 配置为将所有 http 页面重定向到 https,并且该部分有效。但是...假设我有一个页面,https://mysite.com/search/上面有一个搜索表单/按钮。我单击按钮,Django 处理表单,并重定向到结果页面,即http://mysite.com/search/results?term="foo".

此 URL 被发送到浏览器,浏览器将其发送nginx 服务器,该服务器永久重定向到https页面的 -prefixed 版本。(至少我认为这就是正在发生的事情——当然 IE 警告我我要去一个不安全的页面,然后又回到一个安全的页面 :)

但这真的安全吗?或者,至少与标准的纯 HTTPS 站点一样安全?Django 传输 http-prefix URL 的事实是否会损害安全性?是的,据我所知,只有带有 https 前缀的页面才会得到回复,但感觉不对:) 安全性很时髦,正如这个网站可以证明的那样,我担心有什么我失踪了。

4个回答

保护您的 cookie

settings.py 中放置行

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

Cookie 将仅通过 HTTPS 连接发送。此外,您可能还想要SESSION_EXPIRE_AT_BROWSER_CLOSE=True. 请注意,如果您使用的是旧版本的 django(低于 1.4),则没有安全 CSRF cookie 的设置。作为一个快速修复,当会话 cookie 是安全的 ( SESSION_COOKIE_SECURE=True) 时,您可以让 CSRF cookie 是安全的,通过编辑 django/middleware/csrf.py

class CsrfViewMiddleware(object):
   ...
   def process_response(self, request, response):
       ...
       response.set_cookie(settings.CSRF_COOKIE_NAME,
            request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
            domain=settings.CSRF_COOKIE_DOMAIN,
            secure=settings.SESSION_COOKIE_SECURE or None)

将 HTTP 请求定向到 Web 服务器中的 HTTPS

接下来,您需要一个将 http 请求重定向到 https 的重写规则,例如,在 nginx 中

server {
   listen 80;
   rewrite ^(.*) https://$host$1 permanent;
}

Django的reversefunction和url模板标签只返回相对链接;因此,如果您在 https 页面上,您的链接将使您留在 https 网站上。

将操作系统环境变量 HTTPS 设置为 on

最后,(我的原始回复排除了这一点),您需要启用 OS 环境变量HTTPS'on'以便 django 将 https 附加到完全生成的链接(例如,与HttpRedirectRequests 一样)。如果您使用的是 mod_wsgi,则可以添加以下行:

os.environ['HTTPS'] = "on"

到你的wsgi 脚本如果您使用的是 uwsgi,则可以通过命令行开关--env HTTPS=on或将行添加env = HTTPS=on到 uwsgi.ini文件来添加环境变量。作为最后的手段,如果没有其他方法,您可以编辑您的设置文件以包含行import osos.environ['HTTPS'] = "on",这也应该可以工作。

如果您使用的是 wsgi,您可能还需要将环境变量添加wsgi.url_scheme'https'您的settings.py

os.environ['wsgi.url_scheme'] = 'https'

wsgi 建议由Vijayendra Bapte 的评论提供。

您可以通过阅读来了解对这个环境变量的需求django/http/__init__.py

def build_absolute_uri(self, location=None):
    """
    Builds an absolute URI from the location and the variables available in
    this request. If no location is specified, the absolute URI is built on
    ``request.get_full_path()``.
    """
    if not location:
        location = self.get_full_path()
    if not absolute_http_url_re.match(location):
        current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
                                     self.get_host(), self.path)
        location = urljoin(current_uri, location)
    return iri_to_uri(location)

def is_secure(self):
    return os.environ.get("HTTPS") == "on"

其他 Web 服务器事物:

听从那个人的建议,通过在 nginx 中添加一行来打开 Web 服务器中的 HSTS 标头:

add_header Strict-Transport-Security max-age=31536000;

这会告诉您的 Web 浏览器,您的网站在未来 10 年内将仅使用 HTTPS。如果将来从同一浏览器访问任何中间人攻击(例如,您登录咖啡店中的恶意路由器,将您重定向到页面的 HTTP 版本),您的浏览器会记住它应该只是 HTTPS,并防止您无意中放弃您的信息。但是请注意这一点,您不能改变主意,然后决定您的部分域将通过 HTTP 提供服务(直到您删除此行后的 10 年)。所以提前计划;例如,如果您认为您的应用程序可能很快就会流行起来,并且您需要在一个不能以您可以承受的价格很好地处理 HTTPS 的大型 CDN 上,您可能会遇到问题。

还要确保禁用弱协议。将您的域提交给SSL 测试以检查潜在问题(密钥太短、未使用 TLSv1.2、使用损坏的协议等)。例如,在 nginx 中我使用:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

从任何 http:// 重定向到相应的 https:// 页面是错误的方法。配置 nginx 将 80 端口重定向到https://yourdomain.ext/

server {
       listen 80;
       rewrite ^/? https://$host/ permanent;
 }

或类似的(查看您附近的下一个 nginx 手册)并且根本不要在端口 80(http)上运行您的应用程序。因此,端口 80 上的其他请求解析为 404 或类似的(自定义它,说您的应用程序现在是安全的,并且仅在带有指向https://yourdomain.ext/的链接的 https 上运行)。然后仅在侦听端口 443 (https) 上运行您的应用程序。在您的代码中使用相对路径现在是安全的,因为它们都解析为完整的 https:// 路径,并且您避免了 http 到 https 的反弹!

一个常见的设置将让您将 https 流量从您的网络服务器(即 Nginx)转发到运行 Django 应用程序的本地 http 服务器。

在这种情况下,使用该设置会更容易SECURE_PROXY_SSL_HEADER(从 Django 1.4 开始可用。)

https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER

您还应该从 nginx 发送一个HSTS-Header,向客户端(浏览器)表明他们只能使用 HTTPS

add_header Strict-Transport-Security max-age=31536000;