前端安全第二期
什么是XSS
XSS全称(Cross Site Scripting)跨站脚本攻击,是最常见的web应用程序安全漏洞之一,位于OWASP top 10 2013年度第三名,XSS是指攻击者在网页中嵌入客户端脚本,通常是JavaScript编写的危险代码,当用户使用浏览器浏览网页时,脚本就会在用户的浏览器上执行,从而达到攻击者的目的
从上面的一段话,可以得知,xss属于客户端攻击,受害者最终是用户,但特别要注意的是网站管理人员也属于用户之一。这就意味着xss可以进行“服务端”攻击,因为管理员要比普通用户的权限大得多,一般管理员都可以对网站进行文件管理,数据管理等操作,而攻击者一般也是靠管理员身份作为“跳板”进行实施攻击
xss攻击最终目的是在网页中嵌入客户端恶意代码,最常用的攻击代码是JavaScript语言,但也会使用其他的脚本语言,例如:ActionScript、VBScript。而如今的互联网客户端脚本基本上是基于JavaScript,所以如果想要深入研究xss,必须要精通JavaScript
xss换句话说,JavaScript能够到什么效果,xss的胃里就有多大。这完全不是危言耸听。JavaScript可以用于获取用户的cookie,弹出窗口,那么存在xss漏洞的网站,xss就可以用来盗取用户cookie,废掉页面,导航到恶意网站!更高端的xss代码完全可以进行监控你的键盘操作,模仿windows注销界面,诱导你输入开机密码!而攻击者需要做的仅仅是向你的代码中注入JavaScript代码!
前言
XSS的目标是让其他站点的js文件运行在目标站点的上,这主要发生在页面渲染阶段。在该阶段发生了某些非预期的脚本行为,该脚本可能来自用户的输入,也可能来自域外的其他js文件,不一而足。XSS的发生起源来自于用户输入,因此XSS根据用户输入数据以何种形式、何时触发XSS、是否有后端服务器的参与划分为三种类型,分别是反射型XSS、持久型XSS和DOM XSS
反射型XSS(不持久型)
按照字面意思理解,就是需要把黑客的代码反射给浏览器执行,这种攻击一般是用户点击了黑客构造的一个url链接,然后url链接中参数具有攻击性,页面脚本直接按照正常的逻辑去解析了这个参数,导致恶意脚本被执行
之所以称为反射型XSS,则是因为这种攻击方式的注入代码是从目标服务器通过错误信息、搜索结果等等方式“反射”回来 的。而称为非持久型XSS,则是因为这种攻击方式具有一次性。攻击者通过电子邮件等方式将包含注入脚本的恶意链接发送给受害者,当受害者点击该链接时,注 入脚本被传输到目标服务器上,然后服务器将注入脚本“反射”到受害者的浏览器上,从而在该浏览器上执行了这段脚本
比如攻击者将如下链接发送给受害者:
http://www.targetserver.com/search.asp?input=<script>alert(document.cookie);</script>
当 受害者点击这个链接的时候,注入的脚本被当作搜索的关键词发送到目标服务器的search.asp页面中,则在搜索结果的返回页面中,这段脚本将被当作搜 索的关键词而嵌入。这样,当用户得到搜索结果页面后,这段脚本也得到了执行。这就是反射型XSS攻击的原理,可以看到,攻击者巧妙地通过反射型XSS的攻 击方式,达到了在受害者的浏览器上执行脚本的目的。由于代码注入的是一个动态产生的页面而不是永久的页面,因此这种攻击方式只在点击链接的时候才产生作 用,这也是它被称为非持久型XSS的原因
存储型XSS(持久型)
存储型XSS,又称持久型XSS,他和反射型XSS最大的不同就是,攻击脚本将被永久地存放在目标服务器的数据库和文件中
这种攻击多见于论坛,攻击者在发帖的过程中,将恶意脚本连同正常信息一起注入到帖子的内容之中。随着帖子被论坛服务器存储下来,恶意脚本也永久地 被存放在论坛服务器的后端存储器中。当其它用户浏览这个被注入了恶意脚本的帖子的时候,恶意脚本则会在他们的浏览器中得到执行,从而受到了攻击
可以看到,存储型XSS的攻击方式能够将恶意代码永久地嵌入一个页面当中,所有访问这个页面的用户都将成为受害者。如果我们能够谨慎对待不明链接,那么反射 型的XSS攻击将没有多大作为,而存储型XSS则不同,由于它注入的往往是一些我们所信任的页面,因此无论我们多么小心,都难免会受到攻击。可以说,存储 型XSS更具有隐蔽性,带来的危害也更大,除非服务器能完全阻止注入,否则任何人都很有可能受到攻击
典型利用场景(盗取 cookie)
-
通过 XSS 漏洞盗取 cookie 可以说是最典型的利用场景了。不过现在随着 HttpOnly 的广泛应用,这一利用场景也产生了一些限制。但是 HttpOnly 也并不能完全保证 XSS 漏洞的防范,因为 HttpOnly 理论上应该覆盖所有的敏感 cookie,如果有一处没有覆盖到,就有被攻击的可能性。另外一方面,通过结合 CORS 也有突破限制的可能性。还有一个实际情况是,仍然有很多应用并没有使用 HttpOnly,本节也主要是针对这一情形的具体利用
-
假设有一个博客平台,目前我们已经发现博文的评论处存在存储型 XSS 漏洞。那么如果其它用户打开这一篇博文,就可以通过这个漏洞让这名用户执行任意 JS 脚本,基本上就可以进行任何操作。如果这一名用户是管理员的话,那么危害性就更大,理论上这个管理用在这个博客可以执行的任何操作,攻击者都可以实现。这个攻击场景主要分为三步
-
攻击者创建一个 Web 服务,需要保证受害者和这个服务之间是连通的
-
注入 XSS 的 payload,实现通过 JS 获取受害者 cookie,并发送给攻击者创建的服务
-
攻击者通过获取的 cookie 则可以成为应用的合法用户并执行任何操作
-
DOM XSS
DOM XSS不是按照是否存储数据到服务器来划分,它特指通过修改DOM节点的内容,然后触发脚本执行的一种攻击方式,所以当一个反射型XSS通过DOM节点输出脚本控制,它就属于DOM XSS,一个存储型XSS通过DOM节点输出控制,也属于DOM XSS,比如一个网站的搜索引擎输入框,会将你输入的搜索关键词,显示在另一个DOM中,如果没做任何过滤和转义,那么输入的而已内容将被解析,从而导致XSS
比如一个网站的搜索引擎输入框,会将你输入的搜索关键词,显示在另一个DOM中,如果没做任何过滤和转义,那么输入的而已内容将被解析,从而导致XSS
例如提供一个免费的wifi,但是提供免费wifi的网关会往你访问的任何页面插入一段脚本或者是直接返回一个钓鱼页面,从而植入恶意脚本。这种直接存在于页面,无须经过服务器返回就是基于本地的XSS攻击,步骤:
-
提供一个免费的wifi
-
开启一个特殊的DNS服务,将所有域名都解析到我们的电脑上,并把Wifi的DHCP-DNS设置为我们的电脑IP
-
之后连上wifi的用户打开任何网站,请求都将被我们截取到。我们根据http头中的host字段来转发到真正服务器上
-
收到服务器返回的数据之后,我们就可以实现网页脚本的注入,并返回给用户
-
当注入的脚本被执行,用户的浏览器将依次预加载各大网站的常用脚本库
-
这个其实就是wifi流量劫持,中间人可以看到用户的每一个请求,可以在页面嵌入恶意代码,使用恶意代码获取用户的信息,可以返回钓鱼页面
这攻击其实跟网站本身没有什么关系,只是数据被中间人获取了而已,而由于HTTP是明文传输的,所以是极可能被窃取的
XSS的典型案例分析和防御
在分析案例之前,需要了解XSS的两个关键因素:输入源,输出点
-
输入源是XSS攻击中,攻击代码的来源,可以是url,可以是表单内容,可以是事件消息数据等
-
输出点是攻击代码被触发的最直接的方式,比如innerHTML执行,eval执行,javascript:方式触发
url参数攻击
典型的反射性XSS的行为
<html> <head> <title>attack</title> </head> <body> <div> Hello,<span id="username"></span> </div> <script> var userName=decodeURIComponent(milo.request('username')); document.getElementById('username').innerHTML=userName; </script> </body> </html>
假如该页面位于:a.test.com/username.html,那么攻击者可以让用户访问如下链接:
a.test.com/username.html?username=%3Cscript%3Ealert(document.cookie)%3C%2Fscript%3E
即可达到攻击目的,用户访问后,将会弹出用户端cookie
-
输入源
这种攻击的输入源很明显,就是url上的参数
-
输出点
这种攻击的输出点可以是多种,看具体的执行方式,本案例中的输出点是innerHTML执行导致了脚本节点被解析
-
防御
url参数过滤,对url上取到的username参数进行xss过滤,转义,使innerHTML的时候不能被解析成script节点,使用innerText方式,或者jquery的$.text方法写入内容,就不会被解析
表单用户输入攻击
表单输入一般会制造存储型XSS,输入的内容存到了后端,然后再在其它的页面显示出来,显示的时候执行了相关的脚本逻辑,导致被攻击。
例如有某博客网站:www. xxblog.com
写博客的页面在: www. xxblog.com/write.html
看文章的页面在: www. xxblog.com/blogs/xxx.html
下面简要列一下两个页面的核心代码
写博客的页面
<html> <head> <title>写博客</title> </head> <body> <textadrea id="blog_content" placeholder="请输入博客内容"></textadrea> <a class="btn">发布博客</a> <script type="text/javascript"> var blogContent=$('#blog_content').text(); $.ajax({ url:'api/submitBlog.php', type:'POST', data:{ blog:blogContent }, success:function(data){ if(0 == data.iRet){ alert('发表成功'); } } }) </script> </body> </html>
看博客的页面
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" > <title>博客详情</title> </head> <body> <div id="details"></div> <script type="text/javascript"> $.ajax({ url:'api/getBlog.php', type:'GET', data:{ blogId:'xx' }, success:function(data){ if(0 == data.iRet){ //显示文章内容 $('#details').html(data.blogContent); } } }) </script> </body> </html>
-
输入源
本案例中的输入源可以说是网站数据库存储的数据,更源头的应该是存储的内容的来源:写博客的表单节点
-
输出点
本案例中的输出点,同样是innerHTML直接输出,导致了html解析,然后可执行的js呗执行
下图1是本案例中存储型XSS攻击的4个关键环节
-
防御
后端过滤博客内容。存储型xss要过滤的话,最好是在后端过滤,因为后端可以认为是恶意代码源最可靠的拦截阶段了
图3显示了存储型XSS经历4个环节:
(1)前端表单制造内容------(2)提交内容------(3)存储内容-------(4)前端HTML显示存储的内容
其中第2步可以伪造,可以不通过前端页面发布内容。所以过滤逻辑必须在第3步和第4步执行。选择第3步不选择第4步的原因是恶意代码可能会在多个页面(例如图中的PageB,PageC,PageD等)被使用和解析,但是恶意代码值保存一份。所以在入库的时候在后端逻辑中过滤是最直接也是最省事和安全的
第三方组件漏洞
这种类型的XSS危害性很大,第三方组件的漏洞导致的XSS不是特指某一种类型的XSS,而是指页面中引用第三方组件,调用第三方组件的方法时,由于第三方组件的漏洞导致的XSS安全问题
曾经的jquery就曝出了XSS漏洞,在jquery的1.11版本之前,使用jquery的选择器方法,如果传入的选择器字符串是非法的,可能会导致选择器字符串被执行
//假设有以下表单页面a.html供用户填写个人信息 var domId=location.hash; var sTel=$(domId).val();//获取用户输入的电话号码
假如用户点击了非法的链接进入个人信息填写页面,可能会导致XSS漏洞:如下链接就直接会窃取用户的cookie
<a href="./a.html#userinfo<img src=1 onerror=alert(document.cookie)>">填写个人信息</a>
-
本案例中的输入源是url上的hash参数
-
输出点
本案例中的输出点是jquery组件的选择器方法$,导致了恶意参数传入后被解析成HTML执行
-
预防
输入检查,从hash获取的元素id,需要进行过滤,因为id不可能带有括号,冒号,等于号等特殊字符,所以制定相关的策略进行过滤即可
XSS Fuzzing
对于XSS的漏洞挖掘过程,其实就是一个使用Payload不断测试和调整再测试的过程,这个过程我们把它叫做Fuzzing;同样是Fuzzing,有些人挖洞比较高效,有些人却不那么容易挖出漏洞,除了掌握的技术之外,比如编码的绕过处理等,还包含一些技巧性的东西,掌握一些技巧和规律,可以使得挖洞会更加从容。
XSS应该是我挖过的最多漏洞的一种Web漏洞类型,累积下来,就国内BAT、金山、新浪、网易等这些互联网公司的XSS,应该至少也有超过100个,这篇文章主要就是根据自己的一些经验与大家一起探讨编码绕过、处理等技术因素之外的XSS Fuzzing的一些技巧
Fuzzing(模糊测试)是挖掘漏洞最常用的手段之一,不止是XSS,应该可以说Fuzzing可以用于大部分类型的漏洞挖掘。通俗可以把这种方式理解为不断尝试的过程
XSS的Fuzzing流程
这是一个比较常规的Web漏扫中XSS检测插件的一个流程图,其中比较关键的几个点在于:
-
检测输入点
-
潜在注入点检测
-
生成Payload
-
Payload攻击验证
检测输入点其实就是寻找数据入口,比如说GET/POST数据,或者Header头部里的UA/Referer/Cookie,再或者URL路径等等,这些都可以成为输入入口,转换为比较形象点的说法,比如看到一个搜索框,你可能会在搜索框里提交关键词进行搜索,那么这里可能就发生了一个GET或者POST请求,这里其实就是一个输入点
其次是潜在注入点检测,潜在注入的检测是判断输入点是否可以成功把数据注入到页面内容,对于提交数据内容但是不输出到页面的输入点是没有必要进行Fuzzing的,因为即使可以提交攻击代码,也不会产生XSS;在潜在注入点的检测通常使用的是一个随机字符串,比如随机6位数字,再判断这6位数字是否返回输出在页面,以此来进行判断。为什么不直接使用Payload进行判断呢?因为Payload里包含了攻击代码,通常很多应用都有防火墙或者过滤机制,Payload中的关键词会被拦截导致提交失败或者不会返回输出在页面,但这种情况不代表不能XSS,因为有可能只是Payload不够好,没有绕过过滤或者其他安全机制,所以采用无害的随机数字字符就可以避免这种情况产生,先验证可注入,再调整Payload去绕过过滤;而随机的目的在于不希望固定字符成为XSS防御黑名单里的关键词
再者就是生成Payload和进行攻击验证的过程,Payload的好坏决定了攻击是否可以成功;而对于不同情况的注入点,需要使用的Payload也是不同的,比如,注入的位置在标签属性中还是标签事件中,使用的Payload是不同的
标签属性中:如<a href="注入位置">test</a>,Payload:"></a><script>alert(0)</script><a href=" 标签事件中:<img href="a.jpg" onload="注入位置">, Payload:alert(0)
在XSS的世界里有很多的Fuzzing技巧和方式,学会从正常功能中发现攻击方式,在Web安全的世界里,除了技术,还需要猥琐的思维和技巧
另外,其实大家不难发现,不管是昵称、网址跳转或者是文章提到的内容转义的变化也好,很多时候内容原有的地方并不会触发XSS,而内容在其他地方使用后则就触发了XSS,所以对于开发人员来说,不管是来自哪里的内容,都应该有自己的过滤机制,而不能完全的信任,其实总结一句话就是:任何的输入都是有害的