Django Web 应用安全
保护用户数据是任何网站设计中不可或缺的一部分。我们之前在文章Web 安全性中解释了一些常见的安全威胁——本文将通过实际演示说明 Django 的内置保护机制如何处理这些威胁。
先决条件 | 阅读服务器端编程的“网站安全性”主题。完成 Django 教程主题,至少到(包括)Django 教程第 9 部分:使用表单。 |
---|---|
目标 | 了解为了保护 Django Web 应用程序的安全,您需要执行(或不执行)的主要操作。 |
概述
网站安全性主题概述了网站安全性对服务器端设计意味着什么,以及您应该防范的一些常见威胁。该文章的关键信息之一是,几乎所有攻击在 Web 应用程序信任来自浏览器的的数据时都会成功。
警告:关于网站安全,您能学到的最重要的一课就是永远不要信任来自浏览器的的数据。这包括 URL 参数中的 GET
请求数据、POST
数据、HTTP 标头和 Cookie、用户上传的文件等。始终检查和清理所有传入数据。始终假设最坏的情况。
对于 Django 用户来说,好消息是框架处理了许多常见的威胁!Django 中的安全性(Django 文档)文章解释了 Django 的安全功能以及如何保护 Django 驱动的网站。
常见威胁/防护措施
本文不会重复 Django 文档中的内容,而是会在我们 Django 本地图书馆 教程的上下文中演示一些安全功能。
跨站脚本 (XSS)
XSS 是一个术语,用于描述一类攻击,这些攻击允许攻击者通过网站将客户端脚本注入到其他用户的浏览器中。这通常是通过将恶意脚本存储在数据库中来实现的,这些脚本可以被检索并显示给其他用户,或者通过让用户点击一个链接来执行,该链接会导致用户的浏览器执行攻击者的 JavaScript 代码。
Django 的模板系统通过转义 HTML 中“危险”的特定字符来防止大多数 XSS 攻击。我们可以尝试将一些 JavaScript 代码注入到我们的 LocalLibrary 网站中来演示这一点,方法是使用我们在Django 教程第 9 部分:使用表单中设置的创建作者表单。
- 使用开发服务器启动网站(
python3 manage.py runserver
)。 - 在本地浏览器中打开网站,并登录到您的超级用户帐户。
- 导航到作者创建页面(URL 应为:
http://127.0.0.1:8000/catalog/author/create/
)。 - 输入新用户的姓名和日期详细信息,然后将以下文本附加到“姓氏”字段:
<script>alert('Test alert');</script>
。注意:这是一个无害的脚本,如果执行,它将在您的浏览器中显示一个警报框。如果在您提交记录时显示了警报,则表示该网站容易受到 XSS 威胁。
- 按提交保存记录。
- 保存作者后,将显示如下所示。由于 XSS 保护,
alert()
不会运行。相反,脚本将显示为纯文本。
如果查看页面 HTML 源代码,您会发现脚本标签的危险字符已转换为其无害的转义代码等效项(例如,>
现在为 >
)。
<h1>
Author: Boon<script>alert('Test alert');</script>, David
(Boonie)
</h1>
使用 Django 模板可以防止大多数 XSS 攻击。但是,可以关闭此保护,并且该保护不会自动应用于所有通常不会由用户输入填充的标签(例如,表单字段中的 help_text
通常不是用户提供的,因此 Django 不会转义这些值)。
XSS 攻击也可能源自其他不受信任的数据源,例如 Cookie、Web 服务或上传的文件(只要数据在包含在页面之前没有得到充分清理)。如果要显示来自这些来源的数据,则可能需要添加自己的清理代码。
跨站请求伪造 (CSRF) 保护
CSRF 攻击允许恶意用户在未经用户知情或同意的情况下,使用其他用户的凭据执行操作。例如,考虑我们有一个黑客想要为我们的 LocalLibrary 创建更多作者的情况。
注意:显然,我们的黑客并非为了钱!更有野心的黑客可以在其他网站上使用相同的方法执行更具破坏性的任务(例如,将资金转入自己的帐户等)。
为了做到这一点,他们可能会创建一个如下所示的 HTML 文件,其中包含一个作者创建表单(类似于我们在上一节中使用的表单),该表单在文件加载后立即提交。然后,他们会将该文件发送给所有图书管理员,并建议他们打开该文件(它包含一些无害的信息,真的!)。如果任何登录的图书管理员打开了该文件,则表单将使用其凭据提交,并且将创建一个新的作者。
<html lang="en">
<body onload="document.EvilForm.submit()">
<form
action="http://127.0.0.1:8000/catalog/author/create/"
method="post"
name="EvilForm">
<table>
<tr>
<th><label for="id_first_name">First name:</label></th>
<td>
<input
id="id_first_name"
maxlength="100"
name="first_name"
type="text"
value="Mad"
required />
</td>
</tr>
<tr>
<th><label for="id_last_name">Last name:</label></th>
<td>
<input
id="id_last_name"
maxlength="100"
name="last_name"
type="text"
value="Man"
required />
</td>
</tr>
<tr>
<th><label for="id_date_of_birth">Date of birth:</label></th>
<td>
<input id="id_date_of_birth" name="date_of_birth" type="text" />
</td>
</tr>
<tr>
<th><label for="id_date_of_death">Died:</label></th>
<td>
<input
id="id_date_of_death"
name="date_of_death"
type="text"
value="12/10/2016" />
</td>
</tr>
</table>
<input type="submit" value="Submit" />
</form>
</body>
</html>
运行开发 Web 服务器,并使用您的超级用户帐户登录。将上面的文本复制到一个文件中,然后在浏览器中打开它。您应该会收到一个 CSRF 错误,因为 Django 对此类情况有保护措施!
启用保护的方式是在表单定义中包含 {% csrf_token %}
模板标签。然后,此标记将在您的 HTML 中呈现如下所示,其值为当前浏览器中特定用户的特定值。
<input
type="hidden"
name="csrfmiddlewaretoken"
value="0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW" />
Django 生成一个特定于用户/浏览器的密钥,并将拒绝不包含该字段或包含错误字段值的表单(针对特定用户/浏览器)。
要使用这种类型的攻击,黑客现在必须发现并包含特定目标用户的 CSRF 密钥。他们也不能使用向所有图书管理员发送恶意文件并希望其中一人打开它的“散弹枪”方法,因为 CSRF 密钥是特定于浏览器的。
Django 的 CSRF 保护默认情况下处于启用状态。您应该始终在表单中使用 {% csrf_token %}
模板标签,并对可能更改或向数据库添加数据的请求使用 POST
方法。
其他保护措施
Django 还提供其他形式的保护(其中大多数很难或不值得特别演示)
- SQL 注入保护
-
SQL 注入漏洞使恶意用户能够在数据库上执行任意 SQL 代码,从而允许访问、修改或删除数据,而不管用户的权限如何。在几乎所有情况下,您都将使用 Django 的查询集/模型访问数据库,因此生成的 SQL 将由底层数据库驱动程序正确转义。如果您确实需要编写原始查询或自定义 SQL,则需要明确考虑防止 SQL 注入。
- 点击劫持保护
-
在此攻击中,恶意用户劫持了旨在用于可见顶级站点的点击,并将它们路由到下面的隐藏页面。例如,此技术可能用于显示合法的银行网站,但在攻击者控制的不可见的
<iframe>
中捕获登录凭据。Django 以点击劫持保护的形式包含X-Frame-Options
中间件,该中间件在支持的浏览器中可以防止站点在框架内呈现。 - 强制执行 TLS/HTTPS
-
可以在 Web 服务器上启用 TLS/HTTPS,以加密站点和浏览器之间所有流量,包括否则将以明文发送的身份验证凭据(强烈建议启用 HTTPS)。如果启用了 HTTPS,则 Django 提供了一些其他保护措施,您可以使用它们
SECURE_PROXY_SSL_HEADER
可用于检查内容是否安全,即使它来自非 HTTP 代理。SECURE_SSL_REDIRECT
用于将所有 HTTP 请求重定向到 HTTPS。- 使用HTTP 严格传输安全 (HSTS)。这是一个 HTTP 标头,它通知浏览器所有将来对特定站点的连接都应始终使用 HTTPS。结合将 HTTP 请求重定向到 HTTPS,此设置可确保在成功连接后始终使用 HTTPS。HSTS 可以使用
SECURE_HSTS_SECONDS
和SECURE_HSTS_INCLUDE_SUBDOMAINS
或在 Web 服务器上进行配置。 - 通过将
SESSION_COOKIE_SECURE
和CSRF_COOKIE_SECURE
设置为True
来使用“安全”Cookie。这将确保 Cookie 仅通过 HTTPS 发送。
- 主机头验证
-
使用
ALLOWED_HOSTS
仅接受来自受信任主机的请求。
还有许多其他保护措施,以及上述机制的使用注意事项。虽然我们希望这能为您提供 Django 提供的功能概述,但您仍应阅读 Django 安全文档。
总结
Django 对许多常见威胁(包括 XSS 和 CSRF 攻击)具有有效的保护措施。在本文中,我们演示了 Django 在我们的 LocalLibrary 网站中如何处理这些特定威胁。我们还简要概述了一些其他保护措施。
这只是对 Web 安全性的一个非常简短的介绍。我们强烈建议您阅读Django 中的安全性 以获得更深入的了解。
本模块关于 Django 的最后一个步骤是完成评估任务。
另请参阅
- Django 中的安全性(Django 文档)
- Web 上的安全性(MDN)
- 安全实践实施指南(MDN)