WEB攻防系列之XSS

Posted on 2015-05-17 15:46  清荷听雨1989  阅读(84)  评论(0)    收藏  举报

WEB攻防系列之XSS

 

关于XSS,大家应该都有所了解吧。在乌云漏洞报告平台上随处都可见XSS的身影,而且现在XSS的火热程度可以说堪比当年的SQL注入。。。

在XSS刚流行开来的时候,我也一直觉得这东西很神秘的样子,然后到处找这方面的资料恶补,但是因为当时也没接触过网页开发,所以一直都是是懂非懂的,当然,直到接触了网页开发才真正搞懂
这是什么。。。 呵呵~这是题外话啦,但是也说明,要真正搞懂XSS,起码要有网页编程的基础。。。

好了,下面说一下XSS的概念:
XSS,全称Cross Site Script(跨站脚本),因为要与层叠样式CSS区别开来,所以叫XSS(注意XSS拼成XXS了)。。。
其实XSS本质上也是代码注入的问题(这点在前一篇《WEB攻防系列之SQL注入》中已经提到过),但是与SQL注入不同的是,SQL注入是发生在服务端的(WEB服务器),注入的对象是SQL语句;而XSS 是发生在客户端的(用户的浏览器),注入的对象是HTML代码,注入的内容一般是网页脚本(JS、VBS等)或Flash脚本(ActionScript)。。。

上面说SQL注入是发生在服务器的,就意味着SQL注入是威胁到服务器安全的一种攻击,而XSS是发生在客户端的,就意味着是威胁到用户安全、隐私的攻击。。。 早期的XSS攻击是强调跨站的,但因为随着WEB2.0的发展,网页脚本的功能和权限有大大的提高,所以现在的XSS一般是强调脚本这个概念,例如可以利用XSS攻击添加网站管理员的例子(这种攻击一般出现在一些开源的CMS),所以虽然 XSS攻击发生在客户端,但也可能间接的对服务器安全起到威胁。。。

 

XSS攻击会按照它的一些攻击特性进行分类,类别一般有三大类:反射型XSS、存储型XSS、DOM型XSS。。。 下面对这三类XSS攻击进行简单的分析:

 

反射型XSS:

反射型XSS固然像它的命名一样,具有反射的特性,一般就是服务器不对用户提交的数据进行存储,直接输出(常见出现在GET请求上),下面是漏洞的代码:
<?php
$user=$_GET['name'];
echo ‘<a href=”www.xxx.com/user.php?id=’.$user.’”>’.$user.’</a>’;
?>

可以看到这段代码和上面的描述是一样的。。。 程序猿是想让用户输入的数据可以即时进行显示,但还是那句话,程序猿们看到的只是一个功能的实现,而黑阔们看到的是一个漏洞的出现。。。
当用户提交<script>alert(/xss/);</script>的时候,网页会输出什么呢?! 很遗憾,输出的结果是:
<a href=”www.xxx.com/user.php?id=<script>alert(/xss/);</script>”><script>alert(/xss/);</script></a>

输出这段代码就意味着可以利用这个输出任意执行脚本了。 注意,这里所说的执行脚本不单单是进行弹框那么简单(话说,弹个框什么的,显得黑阔们太善良了),这里的弹框只是测试和证明漏洞的存 在,黑阔们可以在这基础上植入一些恶意的脚本。。。
反射型的XSS一般都是通过GET请求的,也就是这种XSS只要仔细查看URL就无所遁形了,所以圈子里不少人都认为这是一种鸡肋的XSS,,但其实反射型的XSS也是有很大的利用价值的,通过一些方法可以增加这种XSS的隐藏性和传播性,我的一个经验就是利用短域名处理的方法对URL进行短域名处理,从而隐藏了真实的URL。。。

 
存储型XSS:

存储型,顾名思义就是存储在服务器上的XSS攻击。这种XSS相对威胁是比较大的,因为它把黑阔提交的恶意代码存储在了服务器上,访问这个网站的用户都可能受到影响,而且如果不把恶意代码去掉,攻击会一直持续下去,所以存储型的XSS也有另一个名称叫做持续型XSS。。。

这种XSS比较典型的攻击例子就是在留言板或博客中插入恶意脚本代码,这样就会导致查看该留言或博客的用户受到攻击。。。

下面是存在存储型XSS漏洞的代码:
<?php
$user=$_POST['name'];
$con = mysql_connect($db_address,$db_user,$db_pass) or die(“不能连接到数据库!!”.mysql_error());
mysql_select_db($db_name,$con);
mysql_query(“insert into user (name) values(‘$user’)”,$con);
mysql_free_result($result);
mysql_close($con);
?>

以上代码是把用户POST的数据存储在数据库中。。。

<div class=”user”>
<?php
$con = mysql_connect($db_address,$db_user,$db_pass) or die(“不能连接到数据库!!”.mysql_error());
mysql_select_db($db_name,$con);
$result = mysql_query(“SELECT name FROM user order by id”,$con);
while($rs=mysql_fetch_array($result))
{
echo ‘$rs["name"]‘;
}
mysql_free_result($result);
mysql_close($con);
?>
</div>
以上的代码是在用户请求页面时,服务器从数据库中读取数据,然后返回到用户请求页面的代码。。。

可以看到,在用户提交数据和请求数据时,都没有对数据进行任何的过滤处理。 当提交:

<script>alert(/xss/);</script>

这是一段JS脚本,而程序并没有对这个数据进行过滤就直接存储在数据库,当用户请求这个页面时,服务器就直接给他返回了这段JS代码,
以下是输出在HTML的代码:
<div class=”user”><script>alert(/xss/);</script></div>

可以看到,这个JS代码已经被插入到了正常的HTML页面中,而浏览器就会把这个数据当作代码来解析了,也就是之前所说的代码注入,相同的,在这里也可以插入一些恶意的JS代码。。。

 
DOM型XSS:

DOM型XSS从产生和效果来说和反射型XSS有着相同之处,它产生的原因是因为修改网页中的DOM节点,再经过网页脚本解析之后产生的,
可以看下面的例子:
<script>
function test(){
ver str=document.getElementById(“text”).value;
document.getElementById(“t”).innerHTML=”<a href=’”+str+”‘>test</a>”;
}
</script>
<div id=”t”></div>
<input type=”text” id=”text” value=”" />
<input type=”button” id=”s” value=”write” onclick=”test()” />
可以看到,网页本身并不包含任何的恶意代码,只是一个正常的网页,但是,在网页的这段JS脚本中,实现的功能是把用户输入的数据插到页面的<a>标签属性中,所以攻击者就可以提交如下语句构造恶意代码:

‘ onclick=alert(/xss/) //
这段代码经过页面的JS处理后,输出到页面的代码是:
<a href=” onclick=alert(/xss/) //’>test</a>

所以XSS就产生了,也就是上面所说的,修改网页中的DOM节点产生的XSS
上面介绍了各种类型XSS产生的原因,那XSS的危害究竟有多大呢?!
可能很多程序猿都有这么一个想法,就是XSS是发生在客户端的,所以对服务端的影响不大,而且修复XSS的难度和工作量也比修复SQL注入等的大,所以大多数程序猿都不大愿意正视XSS这个漏洞,但
要知道XSS攻击所能做的事是非常多的,而且是直接危害到用户安全的,其中常见的有:cookie窃取、收集用户信息、会话劫持、钓鱼攻击、挂马攻击 等的攻击,如果配合一些XSS平台来进行攻击,可以更灵活地实施攻击,所以说,后果也是非常严重的。。。
由于出于安全研究的目的,这里就不展开了,但关于XSS攻击利用的文章,互联网也有许多,有兴趣的可以查阅一下。。。
下面说下XSS的防御:

关于XSS的防御,大多数黑阔只是说:过滤一下就好了。。。 但详细来说应该怎么过滤,大多数就哑口无言了,所以,这里重点讲一下XSS的防御,感谢刺总写的《白帽子讲WEB安全》这本书,为各位开发者提供权威可靠的漏洞解决方案。。。 下面以开发者的角度来分析防御XSS:

 

要防御XSS漏洞,就要回到产生这种漏洞的原因上: 产生XSS的原因是用户提交的数据输出到HTML页面时(MVC中的View层),被当作网页本身的代码执行了(前面所说的代码注入)

所以要防御XSS,程序猿们归结到两种防御方法:

1. 在用户提交数据时,对数据进行编码处理。

2. 在输出页面时,对用户提交的数据进行编码处理。
上面就是常说的输入过滤和输出过滤了,那哪种解决方案要比较好呢?! 下面就对比一下:
第一种:

这种解决方案是比较方便程序猿的,因为这种解决方案只需要进行一次过滤,以后输出的时候就直接输出不需要进行处理了,可以说是一劳永逸,但这种解决方案也存在非常大的问题。。。
因为在用户提交数据的时候就已经把数据进行编码了,而程序输出却可能在各种地方。 这里就出现一个问题了,因为HTML编码和JS的编码方式是不同的,所以相同的编码程序输出到这两个不同的地方时,显示出来的效果也是不同的。
例如,用户提交:爸爸说“早点睡觉” 而经过过滤后,存储进数据库的就变成:爸爸说\“早点睡觉\” ,但当输出到HTML页面时,就会变成:爸爸说\“早点睡觉\”,而输出到JS中才可以正常显示为:爸爸说“早点睡觉” ,所以,对于这种方案来说,选用之前应该先确立输出的地方。。。

当然,这也有例外的,那就是富文本的处理,因为富文本的输出是直接拼接到HTML代码中的,一般不会出现其他特殊的地方。
处理富文本还是需要按照白名单原则,只允许使用安全的标签输出,另外也可以使用一些XSS Filter对用户提交的数据进行智能的XSS过滤,XSS Filter网上也有一些比较成熟的开源项目,有需要的可以Google或百度一下。 另外处理富文本需要注意的是:应该禁止任何脚本事件的输出,因为在富文本不需要任何动态效果,如果加了脚本事件,很可能会产生XSS;还有style属性也应该禁止,style 属性也可能导致XSS的产生。。。

 

第二种:

这种防御方法是在页面输出的时候再对数据进行过滤。 因为是输出到页面时候进行过滤,所以在每个输出的地方都要进行过滤,这自然也增加了程序猿的工作量,那这种防御方法有什么长处么?!
可以看上面第一种防御方法,因为不知道输出的地方就对数据进行处理了,就可能会导致输出到不同的地方,在浏览器显示的效果也不同,而第二种防御方法就不会出现这个问题,因为要在每个输出地方都进行过滤,就可以分析页面输出时在整个页面的语境是怎样的。。。

所以防御XSS应该分不同语境进行,就像刺总说的,在正确的地方做正确的事,那下面看一下输出到页面时,语境的情况分别有什么:
以下情况是在HTML代码中输出的:

<li>$var</li> (标签中输出)

<div class=”$var”></div> (属性中输出,注意不包含事件)

对于这种在HTML代码中输出的,应该对输出的数据进行HtmlEncode

。。。

以下情况是在脚本中输出的:

<script>var a = “$var”;</script> (<script>标签中输出的)

<a href=”#” onclick=”test(‘$var’)”>test</a> (在事件中输出的)

对于在<script>标签中输出的,应该把变量放在“”里面(和第一个例子一样),否则就防御就可以被轻松绕过。
在事件中输出的,防御的方法是对数据进行JavascriptEncode(在事件中输出的,可以再进行一次HtmlEncode),根据浏览器的特性,htmlpersar解析是优先于jspersar的,所以单单对js事件进行htmlencode的话,经过浏览器的解析,编码的事件请求内容就会被解码,从而产生XSS。。。

 

对于在CSS中输出的 则应该尽可能禁止用户在<style>标签或HTML的style属性中输出,如果一定要输出,则一定要遵守白名单原则,也可使用一些安全组织所提供的函数库,刺总在他的书中推荐使用 OWASP ESAPI中的encodeForCSS()函数

 

以下情况是在URL中输出的:

<a href=”$var”>test</a>
对于在URL上输出的,会分有几种情况,因为一个完整的URL会分几个部分:
[协议][主机名][路径][参数][信息]

对应的:http://www.test.com/parth/t.php?id=1#xx

协议:http://
主机名:www.test.com
路径:/parth/t.php
参数:id=1
信息:#xx

在一般情况下在路径和参数中,只需要对数据进行URLEncode就可以,因为编码之后,数据都会编码为%HH的形式,但对于再协议和域名中输出的,又或者是当整个URL都可控制的(以上例子就是整个URL都可控制的),则更复杂一点。
例如输出的数据为javascript:alert(/xss/);
输出到页面之后就是:<a href="javascript:alert(/xss/);">test<a>

对于整个URL的处理,也应该使用白名单处理,先看协议部分是否是允许的协议范围之内,如果不是,就自动加上一个默认的协议,例如http,之后,再对数据进行URLEncode,这样就可以防止XSS的发生。

 

对于处理DOM XSS:
DOM XSS有它的特殊之处,因为它在输出到页面后,可能会再次经过客户端脚本处理进行输出,所以单单对服务器输出的数据进行过滤是不行的。。。
对于DOM XSS的处理,应该是要对所有可能的输出都进行过滤,包括经过客户端脚本处理之后的输出,而过滤的方法则需要看输出到页面的语境来决定, 例如输出到普通的HTML代码中,则进行 HtmlEncode,如果输出到脚本中,则进行JavascriptEncode, 关于在不同语境中如何过滤,可以参考上面的防御方法。。。
另外在HTTP头加入HttpOnly作为防御XSS攻击的一种辅助方法,因为加入了HttpOnly的cookie,客户端脚本是无法获取的,这样就避免了cookie失窃,关于如何加入HttpOnly,不同的语言有不同的方法,大家可以Google或百度一下。。。

 

总结:
虽然XSS的危害程度比SQL注入的危害要小,而且防御起来比SQL注入要麻烦,但XSS是确切关系到用户安全的一种安全漏洞,所以开发人员是不可以忽视的。 这篇文章主要是分析XSS产生的原因和防御的方法,这都是站在开发人员的角度来写的,关于攻击的方法和技巧这里就不展开,因为网上关于XSS攻击的方法和技巧的文章已经很多了,再写如果没有创新也没有什么意义。。。
有读者可能也发现,这篇文章很多内容都涉及到刺总书的内容,那是因为读了刺总的书学到了很多,而且也运用在我开发的网站上,有实在的成果,所以分享给大家,希望可以给大家帮助。。。
另外,文章中多次提到白名单原则。 做安全的应该也要有这样的意识,任何的安全解决方案都尽量使用白名单原则,黑名单会存在很多问题,很容易被绕过,尤其是现在兴起的HTML5中逐渐出现了很多新的标签,如果使用黑名单原则,则不能涵盖所有可能的产生XSS的情况,百度空间曾经就付出了这样的教训。。。
防御XSS的要点就是不信任任何用户提交的数据和不放过任何的输出,这包括程序处理后输出的数据,可以看SAE中曾经出现的一个XSS:http://www.wooyun.org/bugs/wooyun-2010-017604

可以看到在HTTP头也可能会产生XSS,因为HTTP头中一些数据是用户可控的,这点尤其要注意。。。
好了,文章就到这里,如果有什么错漏,欢迎指出。。。

 

原文:http://blog.zcnhonker.net/archives/209