XSS攻击

XSS(Cross-Site Scripting,跨站脚本)攻击历史悠久,最远可以追溯到90年代,但至今仍然是危害范围非常广的攻击方式。在OWASP TOP 10中排名第7。附注 Cross-Site Scripting的缩写本应是CSS,但是为了避免和Cascading Style Sheets的缩写产生冲突,所以将Cross(即交叉)使用交叉形状的X表示。

1)攻击原理

XSS是注入攻击的一种,攻击者通过将代码注入被攻击者的网站中,用户一旦访问网页便会执行被注入的恶意脚本。XSS攻击主要分为反射型XSS攻击(Reflected XSS Attack)和存储型XSS攻击(Stored XSS Attack)两类。

2)攻击示例

反射型XSS又称为非持久型XSS(Non-Persistent XSS)。当某个站点存在XSS漏洞时,这种攻击会通过URL注入攻击脚本,只有当用户访问这个URL时才会执行攻击脚本。我们在本章前面介绍查询字符串和cookie时引入的示例就包含反射型XSS漏洞,如下所示:

@app.route('/hello')
def hello():
    name = request.args.get('name')
    response = '<h1>Hello, %s!</h1>' % name

这个视图函数接收用户通过查询字符串传入的数据,未做任何处理就把它直接插入到返回的响应主体中,返回给客户端。如果某个用户输入了一段JavaScript代码作为查询参数name的值,如下所示:

http://example.com/hello?name=<script>alert('Bingo!');</script>

客户端接收的响应将变为下面的代码:

<h1>Hello, <script>alert('Bingo!');</script>!</h1>

当客户端接收到响应后,浏览器解析这行代码就会打开一个弹窗,如图2-15所示。

你觉得一个小弹窗不会造成什么危害?那你就完全错了,能够执行alert()函数就意味着通过这种方式可以执行任意JavaScript代码。即攻击者通过JavaScript几乎能够做任何事情:窃取用户的cookie和其他敏感数据,重定向到钓鱼网站,发送其他请求,执行诸如转账、发布广告信息、在社交网站关注某个用户等。提示 即使不插入JavaScript代码,通过HTML和CSS(CSS注入)也可以影响页面正常的输出,篡改页面样式,插入图片等。
如果网站A存在XSS漏洞,攻击者将包含攻击代码的链接发送给网站A的用户Foo,当Foo访问这个链接就会执行攻击代码,从而受到攻击。
存储型XSS也被称为持久型XSS(persistent XSS),这种类型的XSS攻击更常见,危害也更大。它和反射型XSS类似,不过会把攻击代码储存到数据库中,任何用户访问包含攻击代码的页面都会被殃及。比如,某个网站通过表单接收用户的留言,如果服务器接收数据后未经处理就存储到数据库中,那么用户可以在留言中插入任意JavaScript代码。比如,攻击者在留言中加入一行重定向代码:

<script>window.location.href="http://attacker.com";</script>

其他任意用户一旦访问留言板页面,就会执行其中的JavaScript脚本。那么其他用户一旦访问这个页面就会被重定向到攻击者写入的站点。

3)主要防范措施

a.HTML转义
防范XSS攻击最主要的方法是对用户输入的内容进行HTML转义,转义后可以确保用户输入的内容在浏览器中作为文本显示,而不是作为代码解析。附注 这里的转义和Python中的概念相同,即消除代码执行时的歧义,也就是把变量标记的内容标记为文本,而不是HTML代码。具体来说,这会把变量中与HTML相关的符号转换为安全字符,以避免变量中包含影响页面输出的HTML标签或恶意的JavaScript代码。
比如,我们可以使用Jinja2提供的escape()函数对用户传入的数据进行转义:

from jinja2 import escape
@app.route('/hello')
def hello():
    name = request.args.get('name')
    response = '<h1>Hello, %s!</h1>' % escape(name)

附注 在Jinja2中,HTML转义相关的功能通过Flask的依赖包MarkupSafe实现。
调用escape()并传入用户输入的数据,可以获得转义后的内容,前面的示例中,用户输入的JavaScript代码将被转义为:

&lt;script&gt;alert(&#34;Bingo!&#34;)&lt;/script&gt;

转义后,文本中的特殊字符(比如“>”和“<”)都将被转义为HTML实体(character entitiy),这行文本最终在浏览器中会被显示为文本形式的,如图2-16所示。

附注 在Python中,如果你想在单引号标记的字符串中显示一个单引号,那么你需要在单引号前添加一个反斜线来转义它,也就是把它标记为普通文本,而不是作为特殊字符解释。在HTML中,也存在许多保留的特殊字符,比如大于小于号。如果你想以文本显示这些字符,也需要对其进行转义,即使用HTML字符实体表示这些字符。HTML实体就是一些用来表示保留符号的特殊文本,比如&lt;表示小于号,&quot;表示双引号。提示 一般我们不会在视图函数中直接构造返回的HTML响应,而是会使用Jinja2来渲染包含变量的模板,这部分内容我们将在第3章学习。
b.验证用户输入
XSS攻击可以在任何用户可定制内容的地方进行,例如图片引用、自定义链接。仅仅转义HTML中的特殊字符并不能完全规避XSS攻击,因为在某些HTML属性中,使用普通的字符也可以插入JavaScript代码。除了转义用户输入外,我们还需要对用户的输入数据进行类型验证。在所有接收用户输入的地方做好验证工作。在第4章学习表单时,我们会详细介绍表单数据的验证。
以某个程序的用户资料页面为例,我们来演示一下转义无法完全避免的XSS攻击。程序允许用户输入个人资料中的个人网站地址,通过下面的方式显示在资料页面中:

<a href="{{ url }}">Website</a>

其中{{url}}部分表示会被替换为用户输入的url变量值。如果不对URL进行验证,那么用户就可以写入JavaScript代码,比如“javascript:alert(’Bingo!’);”。因为这个值并不包含会被转义的<和>。最终页面上的链接代码会变为:

<a href="javascript:alert('Bingo!');">Website</a>

当用户单击这个链接时,就会执行被注入的攻击代码。
另外,程序还允许用户自己设置头像图片的URL。这个图片通过下面的方式显示:

<img src="{{ url }}">

类似的,{{url}}部分表示会被替换为用户输入的url变量值。如果不对输入的URL进行验证,那么用户可以将url设为“123”onerror=”alert(’Bingo!’)”,最终的标签就会变为:

<img src="123" onerror="alert('Bingo!')">

在这里因为src中传入了一个错误的URL,浏览器便会执行onerror属性中设置的JavaScript代码。提示 如果你想允许部分HTML标签,比如,可以使用HTML过滤工具对用户输入的数据进行过滤,仅保留少量允许使用的HTML标签,同时还要注意过滤HTML标签的属性,我们会在本书的第二部分详细了解。附注 你可以访问OWASP的XSS页面( https://www.owasp.org/index.php/Cross-site_Scripting_(XSS) )了解详细的攻击原理介绍和防范措施。

作者:admin  创建时间:2025-02-10 16:30
最后编辑:admin  更新时间:2025-05-15 17:13