同源策略
同源策略是由Netscape提出的一个著名的安全策略。现在所有支持 JavaScript
的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。不同源的客户端脚本( javascript、ActionScript
)在没明确授权的情况下,不能读写对方的资源。
JavaScript的同源策略
JavaScript的同源策略有两点值得注意:
1、包含第三方的脚本是必要的(比如广告、jQuery 等) 2、同源策略对继承自源网站的包含脚本代码有所放松(这两个工作在相同的全局范围内)。
Json hijacking:又被称为JavaScript劫持(如下图所示)
那么我们还能不能找到其他方式可以使得网站的个人数据被泄露?
思路如下:
1、是否有动态的JavaScript文件? 2、如果有,这些文件中是否包含用户数据? 3、这些数据可能会以类似的方式被泄露吗?
首先检测动态的JavaScript文件
这里我们做了个实际的探究,我们尝试在150个热门网站中注册测试账户,然后使用个人化的数据来对该账户数据进行填充,然后用我们的浏览器扩展来与该网站进行彻底的互动以获取这些脚本,最后手动研究这些动态脚本。
对150个网站的JavaScript文件进行了检测——是否有包含用户数据?
以下是检测结果:
XSSI(跨站脚本包含)
Cross Site Script Inclusion (XSSI) 跨站脚本包含是一种允许攻击者通过恶意JS绕过边界窃取信息的攻击技术。
攻击者会将可泄露用户信息的JavaScript文件包含进来:
(1)JavaScript中泄露的用户数据存储在全局变量中
javascript有两种变量:局部变量和全局变量。局部变量是指只能在本变量声明的函数内部调用的变量,而全局变量则是在整个代码中都可以调用的变量。(延伸阅读:http://www.jb51.net/article/61442.htm)
(2)JavaScript中泄露的数据来自全局函数
动态JS
攻击者的JS
对150个网站中的存在泄露信息风险的JS文件是否可以被利用进行了统计,结果如下:
实例探究-案例学习
1、全局变量导致数据泄露
当泄露的数据存储在脚本的全局变量中时,攻击者只要包含了该脚本,就能将该变量添加到当前环境中,攻击者就可以轻松的通过访问该全局变量读取数据。
先看产生数据泄露的脚本文件leak1.js
这里包含有一个全局变量secret
(延伸阅读 Javascript 自执行函数 http://blog.csdn.net/limlimlim/article/details/9198111)
再看攻击者
先引入该leak1.js
然后secret这个变量就进入了本地的环境中
最后再通过window.secret来取得这个变量的值
2、全局功能参数导致数据泄露(1)
许多网站都会将其功能函数分割以写入到不同脚本文件中。常用的功能函数通常会集中在一个基本库中,而实际的业务逻辑函数则被包含在另一个单独的文件中。通常情况下,包含业务逻辑的脚本会调用全局范围内已经注册的那些基本功能函数。攻击者在包含这些脚本的同时,通过重写脚本中的全局函数来实现对泄露数据的操控。
先看产生数据泄露的脚本文件lack2.js
这里有个globalFunction函数会传入secret参数,通过改写该函数来实现获取变量的值
看攻击者
先重写该globalFunction函数,用来输出该变量的值
然后再引入这个产生数据泄露的脚本文件
最后这个globalFunction函数被执行,输出数据
3、全局函数的参数导致数据泄露(2)
同上一个例子类似,漏洞代码调用了一个全局函数然后传递了一些数据给它。然而这次传递给函数的数据是一个包含了我们想要的数据的回调函数,攻击者可以通过使用 toString 方法来获得回调函数中的这些数据。
(延伸阅读:toString() http://www.nowamagic.net/librarys/veda/detail/1743)
先看产生数据泄露的脚本文件lack3.js
我们要的数据在callback()函数中
这里的globalFunction函数将callback作为了参数,如果我们能够重写该globalFunction函数,然后调用toString()方法,就能得到callback()函数中的secret值
看攻击者
先重写globalFunction函数,参数为callback,然后调用toString()方法把callback函数转换为字符类型。(如下举例)
然后再通过正则读取该secret值
最后引入这个产生数据泄露的脚本文件
3、原型链导致数据泄露(1)
利用JavaScript基于原型的继承,对象可以通过指向继承的对象来形成所谓的原型链。比如,一个array实例的原型属性指向全局对象Array.prototype,即再一次指向了Object.prototype。
当一个对象做一个属性查询时,JavaScript引擎会检查该对象是否具有所谓的自定义属性。如果对象本身不包含所请求的属性,JavaScript引擎就会向上一级父节点查询相同的属性。当一个对象具有所要求的属性时,相应的值将被返回,不再向上一级父节点查询。如果这一属性通过向上一级父节点递归查询后还是没有找到,则该引擎将返回属性未定义。
相对于静态范围内的标准变量,这里的关键其实是动态范围。从本质上来讲,这其实意味着如果一个函数被一个对象调用了,那么其实只是意味着其被对象原型中的某一个调用,而不一定是该对象。
此行为可以使得数据虽然无法直接访问,但却可以被一个函数调用。例如我们可以重写Array.prototype.forEach和attacker-controlled函数,如果包含敏感数据的某些代码被forEach函数调用了,attacker-controlled函数将会被包含敏感数据的对象原型中的某一个对象所调用。(这里翻译出来可能会比较晦涩,建议看原文)
(延伸阅读:JavaScript prototype http://www.cnblogs.com/dolphinX/p/3286177.html javascript必知必会prototype http://www.cnblogs.com/mindsbook/archive/2009/09/19/javascriptYouMustKnowPrototype.html )
看完了晦涩难懂,直接看例子:
先看产生数据泄露的脚本文件leak4.js
secret为一个包含数据的数组,然后调用了浏览器的forEach遍历函数
再看攻击者
因为secret为数组,所以我们直接通过prototype属性将数组的forEach函数进行重写来对数据进行操纵
最后再引入泄露数据的脚本文件
这个思路的重点在于对prototype的认识以及对他的利用!
3、原型链导致数据泄露(2)
和上一个例子一样,但是使用了一个函数和toString方法。
先看产生数据泄露的脚本文件leak5.js
再看攻击者
通过prototype将所有Functiorn的call方法重写,然后再调用.toString()方法将函数转换为字符串类型再读取内容,最后再引入产生数据泄露的脚本文件执行。
3、全局函数调用导致数据泄露
如果一个函数在被调用时,能够通过调用者的属性来引用该调用函数。比如一个被命名为"aFunction"的函数,如果通过aFunction.caller.就可以得到调用者的引用。而在函数被引用时,我们可以利用toString方法去返回函数的源代码。如果调用的函数包含任何硬编码的敏感数据,攻击者可以通过解析字符串表示来轻松地访问数据。
同3有点类似
总结
那么如何防止XSSI漏洞?
1、这种攻击方式并不是基于浏览器的,所以不能用基于浏览器的方式来进行防护。
2、防止第三方包含脚本文件
解决方法:严格的调用审查以及使用token等标识
3、从敏感数据中分离出JavaScript 代码
创建静态JS文件并在运行时动态的加载数据
数据服务可通过SOP被保护
需要强调的几点:
1、动态生成JS是非常广泛的做法,许多动态JS文件包含用户 的session 信息,这些脚本文件中包含的数据可被访问。
2、在150个热门网站中有三分之一使用了动态脚本,80%的容易受到 XSSI攻击,通过其可以完全入侵账户获得个人隐私。
3、最后还有一点需要强调,XSSI与内容安全策略(CSP)存在矛盾。
CSP是一个用于防止跨站脚本(XSS)的机制, 其通过白名单的形式来加载信任的JavaScript ,并且要求所有内嵌脚本独立到外部文件中。而动态的内嵌脚本本身是不容易被XSSI的,但外部化之后就会很容易出现XSSI漏洞,因此不要盲目的将脚本独立到外部文件,否则将使得这一问题变得更加糟糕。
原文链接: kittenpics ,翻译:SecDarker,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)