护用户数据是任何网站设计的重要部分。我们之前在文章web安全解释了一些更常见的安全威胁--本文提供了Django的内置保护如何处理这些威胁的实际演示

前提: 阅读服务器端网页编程中的 "Website security" 主题。并请至少完成Django Web框架教程 Django Tutorial Part 9: 使用表单 及以前的教程。
目标: 理解保障Django网站安全应该(和不应该)做的事情。

概述

web安全 主題提供一个概述,说明了网站安全对于服务器端设计的意义,以及以及一些需要应对的常见威胁。本文中包含一个关键的概念:如果网站信任任何来自浏览器的数据,几乎所有的攻击方法都会成功。

重要提示: 切记,对于网站安全来说最重要一点就是“永远不要相信浏览器端提交的数据”。 这些数据包括使用GET方式请求时URL中的参数,POST 方式请求的数据,HTTP headers 和 cookies,以及用户上传的文件等等. 请确保一定要检查和清洗这些提交的数据。对于网站安全来说,总是要做好最坏的打算。

对Django用户来说,好消息是Django框架已经处理了大量的常见威胁。请阅读Django官方文档中的"Security in Django"部分来了解Django的安全细节,以及如何确保基于Django的网站的安全。

常见威胁及保护

在本文中,我们将使用前面章节中的“本地图书馆”项目作为示范来演示一些Django的安全特性。

跨站脚本 (XSS)

XSS(英语:Cross site scripting,通常简称:XSS)是指一类恶意攻击者将代码通过网站注入到其他用户浏览器中的攻击方式。一般攻击者会把恶意代码作为普通数据放入到网站数据库中,这样其他用户在获取和展示数据的过程中就会受到攻击。此外,攻击者还可以通过引诱用户点击某些链接来执行恶意的JavaScript代码。

Django的模板系统可以帮您抵挡大部分的XSS攻击,实现的方式在于转义对于HTML来说比较“危险”的特殊字符(可参考官方文档:escaping specific characters)。现在,我们用Django Tutorial Part 9: Working with forms 这一章中的“创建作者”表单来做个演示,尝试向我们的本地图书馆网站注入一些JavaScript脚本。

  1. 使用开发服务器启动网站(参考命令:python3 manage.py runserver)。
  2. 在浏览器中打开网站,并用超级用户身份登录。
  3. 进入创建作者页面 (地址可能会是:http://127.0.0.1:8000/catalog/author/create/)。
  4. 输入姓名、生日等信息,随后在Last Name这个字段里面填入以下的内容:
    <script>alert('Test alert');</script>
    Author Form XSS test

    注意: 这一段代码并没有任何杀伤力,在执行的时候只会在浏览器中弹出一个警告提示框。如果这个警告提示框出现,则表明本网站存在可被XSS攻击的漏洞。

  5. 点击 Submit 按钮保存信息。
  6. 保存后的作者信息将会显示为下图的样式。因为XSS防护措施的存在,注入代码中的alert()部分并没有执行,而只是用文本的方式直接显示了出来。Author detail view XSS test

如果你有兴趣阅读下页面的HTML源码,则会发现危险的字符已被转义成了无害的字符(例如: > 被转义为了 &gt; )

<h1>Author: Boon&lt;script&gt;alert(&#39;Test alert&#39;);&lt;/script&gt;, David (Boonie) </h1>

Django的模板系统可以帮助抵御大部分的XSS攻击。当然,XSS保护功能也可以被关闭,而且XSS保护一般对非用户输入的内容不会自动进行防护(例如表单中字段的help_text通常不会是用户提交的,所以这部分数据Django也不会进行转义)

XSS攻击也可能来自于其他不可信的数据来源,例如cookies,Web服务或上传的文件(实际上只要是未经清洗的数据直接展示出来都会有被攻击的可能)。如果你要显示这些不可信来源的数据,切记一定要自己做好数据清洗的工作。

防护跨站请求伪造 (CSRF) 

CSRF(英语:Cross-site request forgery,通常简称:CSRF或XSRF)攻击可以让恶意攻击者在用户不知情的情况下,使用用户的身份来进行系统操作。举个例子,现在有一名黑客想要在我们的本地图书馆中添加一些作者信息。

注意:这个示例里面的黑客没有考虑对钱下手。而现实生活中的黑客则极有可能会产生更加危险的操作(例如,把钱转入他们自己的账户中等等)。

为了实现这个目的,黑客可以创建一个类似于下面示例的HTML文件,这个文件包含了一个创建作者的表单(类似我们在之前章节中用过的),并且一旦加载完毕就会立即进行提交。随后黑客可以将这个文件发送至所有的图书管理员,并且引诱他们打开这个文件(文件中真的没有啥有害的信息)。如果任何一个已登录的图书管理员不慎打开了这个文件,那么文件中的表单就会利用图书管理员的身份来提交,随后就会创建出一个新的作者来。

<html>
<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>

运行Django开发服务器,然后使用超级管理员账号进行登录。将上面的代码贴到一个文件中,并在浏览器中打开这个文件,随后你就会看到一个CSRF错误,这是因为Django的安全机制防护了此类的攻击。

在表单定义的时候加入 {% csrf_token %} 这个模板标签, CSRF保护功能即可启用。在模板渲染的时候,这个token在 HTML代码中将会按照下面的格式显示,包含了一个与当前用户和当前浏览器关联的值。

<input type='hidden' name='csrfmiddlewaretoken' value='0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW' />

Django生成这个用户/浏览器关联key的目的在于可以据此来拒绝那些不包含这个key的表单请求,也可以拒绝那些包含了错误了用户/浏览器关联key的表单请求。

有了这种保护机制后,攻击者要发起攻击就需要找到目标用户的CSRF key。通过广撒网给所有的图书管理员发送恶意代码文件的方式也很难奏效,因为CSRF key是和浏览器相关联的。

Django的CSRF防御默认是开启的。一定要在表单的位置使用 {% csrf_token %}这个标签,同时,切记使用POST方式来发起新增和更新数据的请求。

其他防护措施

Django还提供了很多其他形式的防护措施 (大部分不是很容易进行演示):

SQL注入防护
SQL注入漏洞可以让攻击者直接对网站数据库执行构造好的SQL语句,在无需用户权限的情况下即可实现对数据的访问、修改甚至是删除。绝大多数的情况下,使用Django的查询集/模型直接进行数据库访问时,实际使用的SQL语句已经被底层的数据库驱动妥善地进行了转义。如果必须要直接执行自定义的SQL语句,那么也请一定要注意防范SQL注入的问题。
点击劫持防护
点击劫持是指攻击者通过诱导用户,用户本意要访问A网站,最终却访问到了B网站。举例说明,攻击者可以给用户显示一个合法的银行网站,同时把用户名密码登录框改为不可见的<iframe> 标签,以此来窃取用户的登录信息。Django通过 X-Frame-Options中间件来防御点击劫持攻击,在支持的浏览器中,这种方式可以避免网站在iframe中显示。
强制SSL/HTTPS
web服务器可通过启用SSL/HTTPS来加密网站和浏览器之间的所有通信流量,包括了身份认证及其他通过纯文本方式来发送的数据流量(强烈建议启用HTTPS)。如果HTTPS已启用,Django还提供了一起实用的保护措施:
Host头校验
使用 ALLOWED_HOSTS 配置仅接受由信任的host发起的请求。

还有很多其他的安全措施及使用这些安全措施的注意事项我们没有提到。我们这里仅仅提供了Django安全措施的一个概览,更多的信息请参阅Django官方安全文档。

总结

Django具备有效的防护措施,以对抗一些常見的威胁,包括 XSS 和 CSRF 攻击。本文中,我们已经使用本地图书馆网站来了演示Django如何处理一些特定的攻击。我们也提供了关于其它保护措施的简单概述。

但这仅仅是对网站安全的一个入门。我们强烈建议您阅读 Django中的安全 以获得更加深入的理解。

本Django教程的下一步,也是最后一步,是完成 评估任务

参阅

 

In this module

 

文档标签和贡献者

此页面的贡献者: knktc, edgar-chen, xixilog
最后编辑者: knktc,