XSS前端防火墙—天衣无缝的防护

发表于:2014-7-07 11:30

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:邹菜头    来源:51Testing软件测试网采编

  当然,如果配置了框架页的白名单,就能完全避免这回事了。所以这项防御可以选择性的开启。
  事件源
  HTML5 新增了一个叫 EventSource 的接口。不过其用法与 WebSocket 非常相似,因此可以使用类似的钩子进行防御。
  到此,我们列举了各种能执行远程模块的方式。事实上,对其防御并不难,难的是收集这些监控点,做到滴水不漏。
  API 钩子
  对于动态创建的可执行模块,我们通过属性钩子,来监控其远程路径。
  创建元素的方法
  这一节是针对 Chrome 的,因为它不支持原生访问器。
  createElement / createElementNS 无中生有
  cloneNode 克隆现有
  innerHTML / outerHTML 工厂创建
  前两种,通过钩子程序很容易实现。
  第三种,因为 inner/outerHTML 是元素的 property,而非 attribute。由于 Chrome 是无法获取原生访问器的,所以使用钩子会导致无法调用上级接口。
  再者,inner/outerHTML 传进来的是字符串。标签和属性鱼龙混杂,解析字符串肯定是不靠谱的。所以还得先调用原生 innerHTML 批量构建出节点,然后再扫描其中的元素。而这个过程中,节点挂载事件已经触发了。
  所以,无需考虑第三种情况。
  你可能会有疑问,既然用节点挂载事件都能搞定,为什么还要前面的钩子?其实,在第二篇文章 里已经详细讨论了,动态创建的脚本没法被事件拦截,所以才用钩子。
  而通过 innerHTML 产生的脚本,是不会执行的!这个大家都听说过吧。
  修改属性的访问器
  通过原型链的访问器钩子,可以直接监控特定元素的特定 property,完全不影响他人,所以效率非常高。刚才列举了可以执行远程模块的元素,这些元素的路径属性,都得进行重写访问器。
  当然 Chrome 可以忽略这节。
  修改属性的方法
  开头也提到了,除了 setAttribute 外,使用 setAttributeNode 也能设置属性,甚至还有 setAttributeNS 版本的。
  由于 setAttribute 是个经常调用的方法,因此钩子程序必须做足够的优化,将额外的检测消耗降到最低。
  新页面环境
  除了使用最简单的框架,其实还有其他可以获得新页面的途径。
  弹窗
  通过弹窗也能获得新页面环境,大家都知道。但是窗口关闭,也随之销毁了,难道还能使用吗?不妨测试一下:
<style> .aa { color: red }</style>
<button id="btn">POPUP</button>
<script>
btn.onclick = function() {
var win = window.open();
var raw_fn = win.Element.prototype.setAttribute;
win.close();
setTimeout(function() {
console.log(raw_fn);
raw_fn.call(btn, 'class', 'aa');
}, 1000);
};
</script>
Run
  尽管会有瞬间的闪动,但从新窗口里获取的变量确实被保留下来了,并且依然起作用。因为我们引用着它,所以即使窗口关闭,仍然不会对其内存回收的。
  现实中,可以把点击事件绑在 document 上,这样用户随便点哪里都能触发,以此获得纯净的环境。
  因此,我们还得把弹窗函数,也通过钩子保护起来。
  除了最常用的 window.open,其实还有:
  showModalDialog
  showModelessDialog
  opener
  如果当前网页是从其他页面点击打开的,无论是弹窗还是超链接,window.opener 都记录着来源页的环境。
  如果是来源页和自己又是同源站点,甚至还能访问到来源页里面的变量。
  这种情况相当常见。例如从帖子列表页,点开一个帖子详情页,那么详情页是完全可以操控列表页的。
  要解决这个问题也不难,直接给 window.opener 注入防护程序不就可以了,就像对待新出现的框架页那样。
  但是,window.opener 可能也有自己的 opener,一层层递归上去或许有很多。每个页面也许又有自己的框架页,因此防护 window.opener 可能会执行非常多的代码。如果在初始化时就进行,或许会有性能问题。
  事实上,这个冷门的属性几乎不怎么用到。所以不如做个延时策略:只有第一次访问 opener 的时候,才对其进行防护。
  我们将 window.opener 进行重写,把它变成一个 getter 访问器:
var raw_opener = window.opener;
var scanned;
window.__defineGetter__('opener', function() {
if (!scanned) {
installHook(raw_opener);
scanned = true;
}
return raw_opener;
});
  这样,只要不访问 opener,就不会触发对它的防护,做到真正按需执行。
  后记
  关于防护监控点,也没有一个完整的答案,能想到多少算多少,以后可以慢慢补充。
  但是,装了那么多的钩子及事件,对页面的性能影响有多大呢?
  所以,我们还得开发一个测试控制台,来跟踪这套系统。看看监控全开时,会对页面产生多大影响。
33/3<123
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号