跨站脚本攻击(XSS)

概念

XSS攻击,通常指通过'HTML注入'篡改了网页,插入恶意脚本,大多数是javascript或者指向脚本的链接,在用户浏览网页的时候,控制用户浏览器的一种攻击。主要的攻击对象是当前用户的个人信息。

对于XSS攻击者来说,JavaScript工作在渲染后的浏览器环境中,无法控制用户浏览器发出的HTTP头。

XSS的三种类型

  • 反射型XSS:特点是黑客需要诱使用户点击一个恶意链接,才能攻击成功,反射型XSS也叫做‘非持久型XSS’,常用于钓鱼网站,获取用户的重要输入,用户密码等信息。工作原理是黑客给一个链接,这个链接恶意利用了网站的XSS漏洞,比如提交信息是参数是一个恶意脚本,当用户点击链接后,正常的网站已被嵌入恶意脚本,用户后续的操作都有可能受到威胁。

  • 存储型XSS:黑客在一个博客网站发表一个带有恶意javascript代码内容的博客,并被网站当做正常博客保存下来,所有访问该博客的用户,都会在他们浏览器执行这段恶意代码。这种也叫做持久性XSS。

  • DOM Based XSS:将用户输入的数据,用来修改页面的DOM节点造成脚本攻击的方式。

XSS Payload

XSS攻击成功后,即当前浏览的正常网站已经被嵌入恶意脚本。攻击者通过恶意脚本控制用户浏览器,这里的恶意脚本被称为"XSS Payload",实际上就是javascript脚本,所以能通过JavaScript脚本做到的事情,XSS Payload都能做到。

XSS Payload的攻击方式有如下几种:

  1. 获取用户登录cookie,通过想黑客地址请求一张并不存在的图片,并将cookie值作为请求参数。黑客就可以通过自己的请求日志中得到用户cookie值,并通过cookie登录用户的账号进行破坏。

  2. 有的网站会将关键cookie设置httponly属性,这样javascript就无法获取cookie值,但这并不能阻止黑客的行动,我不能登录,那就让你自己登录帮我执行破坏操作就行了,方式就是构建POST、GET请求,进行操作,比如找出删除一篇文章的Url,那么就构建这个Url,让用户不知不觉执行这个url就行了。

  3. 如果上述的功能post方式需要验证码,那么一般的XSS攻击会失效,不过有一些XSS会将验证码的链接发送到远程服务器,解析出验证码后,将值返回给当前的XSS Payload。

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

XSS构造技巧

利用字符编码

如果用户变量中有特殊字符,那么系统会用"\"进行转义,如输入";alert(/XSS/),会被系统转义成:var redirectUrl = "\";alert(/XSS/);"这样参数就是一个字符串,但是如果页面是GBK/GB2312编码,"%c1\"这两个字符组合在一起就是一个Unicode字符,所以如果传递%c1";alert(/XSS/),那么转义后为:var redirectUrl = "%c1\";alert(/XSS/)",%c1会将转义字符\给吃掉,因此造成XSS攻击。

绕过长度限制

一些网站可能会对有可能产生XSS的地方设置变量长度限制,但攻击者可以不插入javascript脚本,而是通过标签自带的onclick事件配合隐藏在location.hash的恶意脚本进行攻击。另一个方式则是通过注释带有长度限制的文本框,打通两个文本框来输入较长字数的javascript脚本。

使用<base>标签

<base>标签用于定义页面上所有使用相对路径标签的hosting地址。攻击者如果在页面插入<base>标签,就可以通过在远程服务器上伪造图片、链接或者脚本,劫持当前页面中所有使用相对路径的标签。

window.name的妙用

对当前窗口的window.name对象赋值,没有特殊字符的限制,因为window对象是浏览器窗口,而并非document对象,因此很多时候window对象不受同源策略的限制,攻击者利用这个对象可以实现跨域,跨页面传递数据。

Anehta的回旋镖

因为浏览器同源策略的原因,XSS也受到同源策略的限制-发生在A域的XSS很难影响到B域的用户,回旋镖的思路就是:如果B域上存在一个反射型XSS_B,A域上有一个存储型XSS_A,当用户访问A域上的XSS_A时,同时嵌入B域的XSS_B,即浏览A网页时,触发A脚本,脚本会跳转到B网站触发B脚本,然后再跳回来,虽然说时间可能很短,但是地址栏可以明显感知到变化。

XSS的防御

利用HttpOnly

浏览器将禁止页面的JavaScript访问带有HttpOnly属性的cookie,在setcookie的时候可以将cookie设置成HttpOnly,设置后JavaScript就无法获取该数据。XSS攻击自然无法奏效了。

输入检查

XSS Filter在用户提交数据㐊获取变量,并进行XSS检查;但此时用户数据没有结合渲染页面的HTML代码,可能对语境理解并不完整。

  1. 比如 <script src = " $var " ></script>,用户只需提交一个恶意脚本的地址就可实施XSS攻击,但是URL在此处本来就是合法用户数据,所以无法判断有没有问题。

  2. 如果输入 1+1<3 ,如果XSS FIlter不够智能有可能将 ‘<’过滤掉,这样改变了用户原本的意思。

  3. 输入的数据还可能展示在多个地方,每个地方的语境可能不同,使用单一替换操作,也有可能出现问题,比如用户输入 我是“天才”,Filter对其专用为$name = ' 我是\"天才\" ',在html代码中展示为:<div>我是 \"天才 \"</div>,在JavaScript中能正常展示。

输出检查

除了富文本的输出外,在变量输出到HTML界面时,可以使用编码或转义方式来防御XSS攻击。

针对HTML代码的编码方式是HtmlEncode,针对JavaScript的则是JavaScriptEncode(后面简称JE)。JE需要使用“\”对特殊字符进行转义,在对抗XSS时,还要求输出的变量必须在引号内部

在正确的地方使用正确的编码方式,列子如下 onclick = "alert(' $var ');",如果用户输入如下:$var = htmlencode("');alert('2"),那么预期是解决了XSS注入,但是!对于浏览器来说,htmlparser会优先于JavaScript Parser执行,也就是alert之前,被HtmlEncode的字符会先被解码,也就是说之前的htmlencode根本没有起到任何的作用,最后执行alert时,里面的数据已经变成"alert('');alert('2')"了,因此第二次alert时就是XSS攻击。

正确的防御XSS

  1. 在HTML标签中输出和属性中输出,都可以使用HtmlEncode防御。在script和事件中输出首先应该确保输出的变量在引号中,防御方式用JE。

  2. 在CSS中输出应尽量禁止,一定有这样的需求可以使用OWASP ESAPI中的encodeForCSS函数。

  3. 在地址中输出,将变量部分进行URLEncode,如果用户能够输入完整的Url,则无法继续使用Urlencode,因为URL的Protocal和Host部分是不能够使用URLEncode的,但是输入完整Url就可能存在构造伪协议实施攻击的风险。如 <a href="javascript:alert(1);">,既然存在这种情况,那么解决方法就是先检查变量是不是‘http’开头,如果不是自动添加,这样可以防止伪协议的攻击。然后再对变量进行URLEncode。

防御DOM Based XSS

DOM based XSS是从javascript中输出数据到HTML界面里,而上述提到的方法都是针对从服务器应用直接输出到HTML界面的XSS漏洞。如下如果对变量进行JE后,又被输出到html页面。

<script>

var x = "JE($var)";

document.write("<a href='"+x+"'>test</a>");

</script>

这样还是会产生XSS攻击,原因在于document.write输出数据到HTML页面时,浏览器重新渲染了页面,在<script>标签执行时,已经对变量x进行了解码,其实JE只保证了第二行赋值语句不会产生XSS攻击,因此write的数据就是根本没进行处理的数据,同理如果使用HtmlEncode于如下代码

<script>

var x = "HtmlEncode($var)";

document.write("<a href='' onclick='alert(\""+x+"\")'>test</a>");

</script>

上述代码仍然会产生XSS攻击。

正确防御的方法是:将变量输出到<script>时,执行一次JE,然后document.write等操作将其输出到HTML界面时,如果输出到事件或者脚本,则再做一次JE,如果是输出到HTML内容或者属性,则再做一次HTMLEncode。

 

浏览器解码流程

  1. url解析

  2. 使用html解析器将标签构建成DOM解析树,此时里面的数据还没有进行html解码。

  3. DOM树构建成功后,会对节点内容进行解析转码。

  4. 运行css解析器。

  5. DOM节点已经构建完成后,处理到 <script>、<style>等标签时会触发javascript解析器,触发解析器的方式还有:伪协议中URL绑定、事件处理器(onclick、conerror、onload)、定时器、eval等,javascript解析器会将unicode类的数据进行解码。

如果遇到如下情况:

<script>

document.getElementById("1").innerHTML = "<img src=# on\u0065rror=alert(1)>";

</script>

相当于改变了DOM树,那么首先会触发javascript解析器,解码<img src = .....>部分,然后修改DOM树后,会重新对这一块走一遍步骤 2、3、4、5。也就是再进行html解码,然后onerror触发javascript解析器再进行javascript解码。

浏览器解码过程

猜你喜欢

转载自blog.csdn.net/github_38392025/article/details/81708081
今日推荐