跨站点脚本 (XSS)

在Web应用中, 跨站点脚本 (XSS)有时在被渲染成HTML之前,不能恰当地对用户提交的内容进行转义。 这使得攻击者能够向你的网站页面插入通常以 <script> 标签形式的任意HTML代码。

攻击者通常利用XSS攻击来窃取cookie和会话信息,或者诱骗用户将其私密信息透漏给被人(又称 钓鱼 )。

这种类型的攻击能够采用多种不同的方式,并且拥有几乎无限的变体,因此我们还是只关注某个典型的例子吧。 让我们来想想这样一个极度简单的Hello World视图:

  1. from django.http import HttpResponse
  2. def say_hello(request):
  3. name = request.GET.get('name', 'world')
  4. return HttpResponse('<h1>Hello, %s!</h1>' % name)

这个视图只是简单的从GET参数中读取姓名然后将姓名传递给hello.html模板。 因此,如果我们访问 http://example.com/hello/?name=Jacob ,被呈现的页面将会包含一以下这些:

  1. <h1>Hello, Jacob!</h1>

但是,等等,如果我们访问 http://example.com/hello/?name=&lt;i&gt;Jacob&lt;/i&gt; 时又会发生什么呢?

  1. <h1>Hello, <i>Jacob</i>!</h1>

当然,一个攻击者不会使用<i>标签开始的类似代码,他可能会用任意内容去包含一个完整的HTML集来劫持您的页面。 这种类型的攻击已经运用于虚假银行站点以诱骗用户输入个人信息,事实上这就是一种劫持XSS的形式,用以使用户向攻击者提供他们的银行帐户信息。

如果您将这些数据保存在数据库中,然后将其显示在您的站点上,那么问题就变得更严重了。 例如,一旦MySpace被发现这样的特点而能够轻易的被XSS攻击,后果不堪设想。 某个用户向他的简介中插入JavaScript,使得您在访问他的简介页面时自动将其加为您的好友,这样在几天之内,这个人就能拥有上百万的好友。 在几天的时间里,他拥有了数以百万的朋友。

现在,这种后果听起来还不那么恶劣,但是您要清楚——这个攻击者正设法将 的代码而不是MySpace的代码运行在 的计算机上。 这显然违背了假定信任——所有运行在MySpace上的代码应该都是MySpace编写的,而事实上却不如此。

MySpace是极度幸运的,因为这些恶意代码并没有自动删除访问者的帐户,没有修改他们的密码,也并没有使整个站点一团糟,或者出现其他因为这个弱点而导致的其他噩梦。

解决方案

解决方案是简单的: 总是转义可能来自某个用户的任何内容。

为了防止这种情况,Django的模板系统自动转义所有的变量值。 让我们来看看如果我们使用模板系统重写我们的例子会发生什么

  1. # views.py
  2. from django.shortcuts import render_to_response
  3. def say_hello(request):
  4. name = request.GET.get('name', 'world')
  5. return render_to_response('hello.html', {'name': name})
  6. # hello.html
  7. <h1>Hello, {{ name }}!</h1>

这样,一个到http://example.com/hello/name=Jacob 的请求将导致下面的页面:

  1. <h1>Hello, &lt;i&gt;Jacob&lt;/i&gt;!</h1>

我们在第四章涵盖了Django的自动转义,一起想办法将其关闭。 甚至,如果Django真的新增了这些特性,您也应该习惯性的问自己,一直以来,这些数据都来自于哪里呢? 没有哪个自动解决方案能够永远保护您的站点百分之百的不会受到XSS攻击。