Django 的安全性

此文档是对 Django 安全性特征的概述。它包含了对保障使用 Django 驱动的网页安全的建议。

防御跨站脚本攻击(XSS)

XSS 攻击让其使用者可以向其他用户的浏览器中注入客户端脚本。它通常由在数据库中存储恶意脚本的方式来实现,这些脚本会在数据库中被检索并显示给其他用户;或通过用户点击那些会引发攻击者的 JavaScript 脚本被用户浏览器执行的链接来实现。然而,倘若在数据加载到页面之前未经过彻底的清理,那么 XSS 攻击可以来自任何不可信任的数据源,比如 cookies 或者 Web 服务器。

使用 Django 的模板可以保护您免受大多数XSS攻击。但是了解它提供了怎样的保护,以及有什么限制等是很重要的。

Django 模板 escape specific characters 对于 HTML 来说是很危险的。这保护着用户免受多数恶意输入的攻击,但并非万无一失。比如,出现下面这种情况就会保护失效:

<style class={{ var }}>...</style>

如果 var 被设置为 'class1 onmouseover=javascript:func()' ,将导致未经授权的 JavaScript 脚本执行,这取决于浏览器如何呈现不完美的HTML。(引用属性值可以解决这个问题)

同样重要的,还有在 is_safe 与自定义模板标签,safe 模板标签,mark_safe 一起设置时,以及关闭自动转义时要特别小心。

此外,如果使用模板系统输出了除 HTML 之外的内容,可能会有完全独立的字符和单词需要转义。

您在将 HTML 储存到数据库中时也要非常小心,特别是在检索和显示 HTML 的时候。

防御跨站点请求伪造(CSRF)

CSRF 攻击让恶意用户可以使用别的用户的证书执行操作,且是在其不知情或不同意的情况下。

Django 已经内置了保护措施来对抗大多数 CSRF 攻击,您需要在合适的地方 enabled and used it 。但和多数缓解性技术一样,它是有局限性的。比如可以全局禁用 CSRF 模块或者特定的视图。如果您真的想这么做,请三思而后行。如果您的网页还有脱离您控制的子域,还将会有其他 limitations

CSRF 保护机制 通过检查每一个 POST 请求中的密文来实现。这保证恶意用户不能“复现”一个表单并用 POST 提交到你的网页,并让一个已登录用户无意中提交该表单。恶意用户必须知道特定于用户的密文(使用 cookie)。

在部署 HTTPS 时,CsrfViewMiddleware 会检查 HTTP 报文的 referer 首部是否设置为同源的 URL(包括子域和端口)。因为 HTTPS 提供了额外的安全性,所有通过转发不安全连接请求并在支持的浏览器中使用 HSTS 来确保连接在可用的地方使用了 HTTPS ,这一点是很重要的。

除非绝对需要,否则对视图进行标记 csrf_exempt 装饰器时要极其慎重。

防御 SQL 注入

SQL 注入是一种让恶意用户能在数据库中执行任意 SQL 代码的攻击方式。这将导致记录被删除或泄露。

Django 的 querysets 在被参数化查询构建出来时就被保护而免于 SQL 注入。查询的 SQL 代码与查询的参数是分开定义的。参数可能来自用户从而不安全,因此它们由底层数据库引擎进行转义。

Django 也为开发者提供了书写 raw queries 或执行 custom sql 的权利。应当尽可能少地使用这些方法,并且您应该小心并准确的转义一切用户可控的参数。另外,在使用 extra()RawSQL 时应当小心谨慎。

防御访问劫持

访问劫持是一种让恶意网页能覆盖另一个网页框架的攻击方式。这将导致毫不知情的用户被骗入目标网页并执行意料之外的操作。

Django 包含 clickjacking protection ,以 X-Frame-Options middleware 的形式在支持它的浏览器中阻止一个网页被渲染在 frame 的内部。可在每个视图的基础上禁用保护,也可配置发送的确切头部值。

对于任何不会被第三方网站嵌入 frame 的网页,或者只允许使用一小部分的网页来说,强烈建议使用中间件。

SSL/HTTPS

将您的网页通过 HTTPS 部署,对于保障安全性来说是最佳的。没有它,恶意网络用户就可以在客户端和服务器之间嗅探验证资格或其他任何信息,在某些情况下 -- 主动 网络攻击者 -- 会修改发向其中任何一方的数据。

如果您想得到 HTTPS 的保护,且已经在您的服务器上启用了,下面还有一些额外的步骤需要执行:

  • 如果有必要,设置 SECURE_PROXY_SSL_HEADER,确保您已经彻底的了解了它的警告。如果不这么做,将会导致 CSRF 漏洞,如果操作不正确,也是非常危险的。

  • 设置 SECURE_SSL_REDIRECTTrue,这样 HTTP 的请求就会被重定向到 HTTPS。

    请注意 SECURE_PROXY_SSL_HEADER 下的警告。对于反向代理,设置主服务器来重定向到 HTTPS 会更简单且更安全。

  • 使用 'secure' cookies。

    如果浏览器使用默认的 HTTP 来实现初始连接,可能会导致已有的 cookies 泄露。因此,您应当将 CSRF_COOKIE_SECURECSRF_COOKIE_SECURE 设置为 True。这会让浏览器仅用 HTTPS 连接来发动这些 cookies。注意,这让 sessions 不能再通过 HTTP 工作,且 CSRF 防御机制将会阻止任何通过 HTTP 接收到的 POST 数据(当然把所有 HTTP 都弄成 HTTPS 是最好的)。

  • 使用 :ref:`http-strict-transport-security`(HSTS)

    HSTS 是一个 HTTP 头部,它使得浏览器未来总是在连接到某特殊网页时使用 HTTPS。结合将请求从 HTTP 重定向到 HTTPS,一旦一个连接被成功建立,就能保证之后的连接总能受到 SSL 提供的额外安全保证。HSTS 或者在 Web 服务器上配置,或者通过 SECURE_HSTS_SECONDSSECURE_HSTS_INCLUDE_SUBDOMAINS,以及 SECURE_HSTS_PRELOAD 来进行配置。

Host 头部验证

在某些情况下,Django 使用客户端提供的 Host 头部来构造 URLs。这些值虽被清理以阻止跨站脚本攻击,但伪造 Host 值还是可以用于跨站请求伪造,缓存毒化攻击,以及电子邮件中的有毒链接。

因为即使看起来安全的服务器配置也容易受到假的 Host 头部信息的影响,Django 依靠定义在 django.http.HttpRequest.get_host() 方法中的 ALLOWED_HOSTS 来验证 Host 头部。

这些验证仅通过 get_host() 来实现;如果您的代码直接从 request.META 得到 Host 头部,您就绕过了这种安全保护机制。

更多细节请参照完整的 ALLOWED_HOSTS 文档。

警告

本文档以前的版本建议配置你的网络服务器,以确保它验证传入的 HTTP Host 头。虽然这个建议仍然有效,但在许多常见的 Web 服务器中,看似验证了 Host 头的配置,实际上可能并没有验证。例如,即使 Apache 的配置是让你的 Django 网站从一个非默认的虚拟主机上运行,并设置了 "ServerName",HTTP 请求仍然有可能匹配这个虚拟主机,并提供一个假的 "Host " 头。因此,Django 现在要求你显式地设置 ALLOWED_HOSTS,而不是依赖 Web 服务器的配置。

另外,如果您的配置有需求,Django 要求您明确启用对 X-Forwarded-Host 标头的支持(通过 USE_X_FORWARDED_HOST 配置)。

Referrer 策略

浏览器使用 Referer 头部来把关于用户是如何到达哪里的信息发送到网页。通过设置 Referrer 策略,限制在哪些情况下设置 Referer 头部,您可以保护您的用户的隐私。请看 :ref:` 安全中间件参考书的 referrer 策略部分<referrer-policy>` 了解更多细节。

会话安全

类似于 CSRF 限制 要求一个被部署的网页应让不受信任的用户不能访问任何子域,django.contrib.sessions 也有限制。参照 the session topic guide section on security 获取更多细节。

用户上传内容

注解

考虑从云服务或 CDN 提供静态文件服务来避免此类问题。

  • 如果您的网站接受文件上传,强烈建议您在服务器配置中将这些上传限制在合理大小范围中,以此来防御拒绝服务(DOS)攻击。在 Apache 中,使用 LimitRequestBody 指令可以很容易地实现这个设置。

  • 如果您为自己的静态文件提供服务,确保像 Apache 的 mod_php 这种能把静态文件当作代码来执行的处理程序已经被关闭。您绝不会希望用户能够通过上传并请求特质文件来执行任意的代码。

  • 如果媒体文件没有遵循安全性最佳惯例,Django 的媒体上传处理会产生一些漏洞。特别的,如果一个 HTML 文件包含合法的 PNG 格式头部并附加一些恶意的 HTML,它是可以作为一个图片文件上传的。该文件将会通过 Django 用于 ImageField 图片处理(Pillow)库的验证。当此文件随后被展示给用户时,它可以被显示为 HTML,这取决于您的服务器类型于配置。

    在框架级别上没有防弹技术方案可以安全地验证所有用户上传的文件内容,但是您可以采取其他步骤来减轻这些攻击:

    1. 通过一直为来自不同顶级域名或二级域名的用户提供上传内容可以防御一类的攻击。这可以防止被 same-origin policy 保护机制阻止的任何攻击,比如跨站脚本攻击。例如您的网站是 example.com,您应当通过形如 usercontent-example.com 的方式来提供上传内容服务(配置 MEDIA_URL)。仅仅从像 usercontent.example.com 这样的子域提供内容是*不够*的。
    2. 除此之外,应用可以选择定义一个列表来限制允许用户上传的文件的扩展名,并将 Web 服务器配置为仅为此类文件服务。

其他安全性相关主题

尽管 Django 提供了开箱即用的良好安全保护,正确部署应用程序并利用 Web 服务器,操作系统和其他组件的安全保护仍然很重要。

  • 确保您的 Python 代码位于 Web 服务器的根目录之外。这将确保您的 Python 代码不会意外地被用作纯文本(或意外地被执行)。
  • 小心一切 用户上传的文件
  • Django 不会限制对用户进行身份验证的请求。为了防止对身份验证系统的暴力攻击,您可以考虑部署 Django 插件或 Web 服务器模块来限制这些请求。
  • 保持 SECRET_KEY 是密文。
  • 用防火墙限制缓存系统和数据库的可访问性是个好主意。
  • 看一下开源 Web 应用安全计划(OWASP) Top 10 list ,它指定了网络应用程序中一些常见的漏洞。尽管 Django 拥有解决某些问题的工具,但在项目设计中必须考虑其他问题。
  • Mozilla 讨论了很多与 web security 相关的主题。他们的网页还包括适用于任何系统的安全原则。
Back to Top