【XSS】利用 onload 事件监控流量劫持
说到跨站资源监控,首先会联想到『Content Security Policy』。既然 CSP 好用,我们何必自己再搞一套呢。那就先来吐槽下 CSP 的缺陷。
目前的 CSP
日志不详细
用过 CSP 的都很郁闷,上报的只有违规的站点名,却没有具体路径。这是缺陷,还是特意的设计?
显然,CSP 是为安全定制的,里面的规范自然要严格制定,否则就会带来新的安全问题。如果支持详细路径的上报,那又会引出什么问题?
由于 CSP 会上报所有的请求,甚至包括重定向的,因此可以用来探测重定向后的地址。假如已登录的用户访问 login.xx.com 会重定向到 xx.com/username,那么攻击者设计一个只允许重定向前的规则的页面,用户访问后,重定向后的 URL 就会当做违规地址上报给攻击者,这其中就包括了用户名。
如果支持详细路径的上报,这简直就是灾难,就用来探测的用户隐私信息了。事实上目前只上报主机名,都能进行一些利用,例如这篇 Using Content-Security-Policy for Evil。
不过新的规范总是在改进,未来也许只上报重定向前的 URL。但在这之前,我们只能接受这些鸡肋的上报日志。
规则不灵活
CSP 目前只支持白名单列表,这多少有些死板。
更糟的是,不同规则之间无法继承和共享。例如默认有个 default-src
规则,但其他的规则会覆盖它,而不是继承它。这就导致各个规则之间,出现很多的重复,使得整个字符串变的冗长。
无法和页面交互
CSP 的监控和上报,是在浏览器后台自动处理的,没有提供一个事件供页面进行交互。
这样就只能使用统一方式强制处理了,而无法交给页面脚本,更好的来自定义处理。
上报方式不可控
如果处理方式有多种选择,那么统一处理也无可厚非。
但事实上 CSP 的上报方式及格式,没有任何可选余地。只能使用 POST + JSON 的方式提交,并且其中的字段十分累赘,甚至把规则里的白名单列表也发上来了。
此外,也无法设定一个缓存时间,控制重复上报的间隔。在配置白名单遗漏时,会出现大量的误报,严重消耗资源。
浪费带宽
在较新的 Chrome 里,能够使用 meta 标签在前端页面定义 CSP 规则,但其他浏览器目前仍不支持。
为了能够统一,大多仍使用 HTTP 头部输入的方式。由于规则通常都很长,导致每次页面访问,都会额外增加数百字节。
维护繁琐
如果是通过 Web 服务开启的,那么每次调整策略,都得修改配置甚至重启服务,很是麻烦。
兼容性不高
目前只有高版本的浏览器支持,而 IE 系列的则几乎都没能很好的支持。
如果某些攻击只争对低版本的浏览器,那么很有可能出现大量遗漏。
模拟的 CSP
原理
事实上在 CSP 出现的好几年前,就有一个能够监控跨站资源的方案,下面就来分享下。
写过 JS 的都知道,如果需要给大量元素监听事件,无需对每个元素上都进行绑定,只要监听它们的容器即可。当具体的事件冒泡到容器上,通过 event.target 即可获知是哪个元素产生的。
脚本、图片、框架等元素加载完成时,都会产生 onload 事件;而所有元素都位于『文档』这个顶级容器。因此我们监听 document 的 onload 事件,即可获知所有加载资源的元素。
不过 onload 这个事件比较特殊,无法通过冒泡的方式来监听。但在 DOM-3 标准模型里,事件还有一个『捕获』的概念,这也是为什么 addEventListener 有第三个参数的原因。
我们通过事件捕获机制,将其拿下,从而监控文档级别的全局 onload 事件。
<script>
document.addEventListener('load', function(e) {
console.log(e.target);
}, true);
</script>
<script src="https://libs.baidu.com/jquery/1.9.0/jquery.js"></script>
<iframe src="https://www.baidu.com/"></iframe>
类似的,如果资源加载失败,会触发 onerror 事件。我们也可同时将其捕获,跟踪那些暂时不可用的跨站资源。
优势
通过脚本的方式,就可以更灵活的处理问题了。规则的黑白名单,上报方式、格式等等,都可以自己来定义。最重要的是,我们能够获得详细的违规 URL 了!
相比后端配置,前端脚本更新维护起来容易的多。而且得益于浏览器缓存,无需每次更新配置,节省很多资源。
增强
也许你已经发现了,这只能实现 CSP 的 Report-Only 功能,根本无法进行拦截。而且其他的内联事件、网络通信等也没有涉及。
不过本文的主题已经说了,只是监控跨站资源而已,并不拦截。事实上,如果能够做到及时发现问题,就很不错了。
如果非得通过 JS 来实现拦截功能,可以参考之前的『XSS 前端防火墙系列』:
虽然可以更严格的防护,但实现起来更臃肿,性能开销也更大。要是拦截了正常的业务功能,造成的损失会更大。
而使用如今这个小技巧,只需几行代码即可实现,性能消耗忽略不计。
缺陷
当然,那么简单的方案肯定无法全面。
由于我们只监听文档容器,有些还未加入到文档的元素产生的事件,我们就无法捕获到了。最典型的就是:
new Image().src = '...'
虽然创建的 HTMLImageElement 对象具有 onload 事件,但是此时还只是一个离屏元素,事件就无法对外传播了。
如果非得解决,只能通过函数钩子的方式监控 URL。
此外,IE9 以下的浏览器不支持 DOM-3,而 attachEvent 是无法设置捕获的,因此我们还需一个后备方案。毕竟国内低版本 IE 用户仍有不少,即使能实现部分功能,也胜于无。
后备
事实上,即使主流浏览器,有些特殊元素并没有 onload 事件,例如 Flash 插件。
为了弥补这些不足,同时尽可能保持简单高效,我们使用定时轮询的方式,对特定元素进行扫描。这里使用一个大家都知道,但未必都清楚的方法:document.getElementsByTagName。
这个功能都知道,但返回的类型或许很少琢磨。他返回的并不是一个 Array,也不是 NodeList,而是 HTMLCollection。
在 W3C 规范描述中,有一个显眼的词 『live』,已经道出了这个接口的独特之处——它是一个动态的集合,能随着容器内元素的增减而变化。
因此,我们事先映射出文档容器内的元素,之后即可随时查询集合了。
<button id="btn">Load Script</button>
<div id="stat"></div>
<script>
function log(str) {
var line = document.createElement('div');
line.innerHTML = str;
stat.appendChild(line);
}
// 这是个动态集合,只需查询一次即可!
var colScript = document.getElementsByTagName('script');
setInterval(function() {
var display = [];
// 可以访问到最新的记录
for (var i = colScript.length - 1; i >= 0; i--) {
var el = colScript[i];
if (el.src) {
// check url
display.push(el.src);
}
}
log('num: ' + display.length + ' list:' + display.join(',') );
}, 1000);
// [test] load script
btn.onclick = function() {
var el = document.createElement('script');
el.src = 'http://libs.baidu.com/jquery/1.9.0/jquery.js';
document.body.appendChild(el);
}
</script>
除了 SCRIPT,我们还可以监控其他存在隐患的元素,例如:EMBED,OBJECT,IFRAME 等等。这样即使是低版本的 IE 用户,也能参与预警上报了,比起完全没有好的多。
当然,这种简单方法也很容易被绕过。如果脚本删除了自身元素,那么我们就无法跟踪到了。而脚本一旦运行就已在内存里,即使元素节点被移除,仍然能继续运行。
不过,对于一般的情况也足够应对了。通常运行商的广告劫持,大多都很落后。即使要进行后期对抗,也可以利用之前的前端防火墙,使用严格的方案。
扩展信息
见过 CSP 日志的大多都会很困惑,出现的这些违规资源,究竟位于页面何处。由于没有确切的细节,给排查工作带来很大困难。
毕竟,绝大多数的上报问题,都是无法复现的。它们要么是运行商的广告,或者是浏览器插件。难很通过这些日志,来定位问题所在。
既然如今使用自己的脚本来实现,理应带上一些有意义的信息。除了资源类型和详细 URL,我们还需要一个能够定位问题所在的参数——DOM 路径。
将每层元素的 #id 和 .class 跟随标签名,得到一个标准的 CSS 选择器。通过它,即可非常容易的定位到违规元素,同时也能用于统计和分析。
例如出现顶级元素就是 SCRIPT
的,显然这是一个被插入到 <html>
之外的脚本,很有可能就是运营商注入的广告脚本。
例如出现 HTML > BODY > ... > DIV.editor-post > SCRIPT
这样帖子容器里的脚本,那么极有可能是出现 XSS 了。正常情况下,用户内容区域并不会出现脚本元素。
通过详细的上报日志不断学习,后端即可越来越精准的分析出问题所在。
而这些,目前的 CSP 难以实现。
上报方式
吸取 CSP 报告的不足之处,我们使用更灵活的方式。
对于运营商广告那样的跨站资源,反复上报同样的信息,是毫无意义的,只会浪费带宽资源。对于同一类警告,一定时间内上报一次就后够了。
利用 URL、DOM 路径等信息,可以更容易的将日志进行客户端去重。通过本地存储的记录,就不必每次都上报了,在本地记录违规的次数即可。
对于不严重的警告,本地也可以累计到一定的数量再上报。这样多条日志一次发送,即可大幅减轻后端压力。甚至还可以考虑压缩内容,节省网络带宽。
只有让前端进行负载维护,才不至于大规模部署的场合下,接收端被海量的误报日志拖垮。
总结
尽管 CSP 的初衷很美好,但到如今,仍只是能用,并没有做到好用。因此,实际面临的问题,还是得靠自己来解决。
当然,标准的制定本来就是受益于大众的,我们也希望 CSP 标准能发展的越来越好用。
【XSS】利用 onload 事件监控流量劫持的更多相关文章
- 利用 onload 事件监控跨站资源
用过 CSP 的都很郁闷,上报的只有违规的站点名,却没有具体路径.这是缺陷,还是特意的设计? 显然,CSP 是为安全定制的,里面的规范自然要严格制定,否则就会带来新的安全问题.如果支持详细路径的上报, ...
- [JS5] 利用onload执行脚本
<html> <head> <title>利用onload执行脚本</title> <SCRIPT TYPE="text/JavaScr ...
- 【流量劫持】SSLStrip 的未来 —— HTTPS 前端劫持
前言 在之前介绍的流量劫持文章里,曾提到一种『HTTPS 向下降级』的方案 -- 将页面中的 HTTPS 超链接全都替换成 HTTP 版本,让用户始终以明文的形式进行通信. 看到这,也许大家都会想到一 ...
- Linux-某电商网站流量劫持案例分析与思考
[前言] 自腾讯与京东建立了战略合作关系之后,笔者网上购物就首选京东了.某天在家里访问京东首页的时候突然吃惊地发现浏览器突然跳到了第三方网站再回到京东,心里第一个反应就是中木马了. 竟然有这样的事,一 ...
- 【流量劫持】躲避 HSTS 的 HTTPS 劫持
前言 HSTS 的出现,对 HTTPS 劫持带来莫大的挑战. 不过,HSTS 也不是万能的,它只能解决 SSLStrip 这类劫持方式.但仔细想想,SSLStrip 这种算劫持吗? 劫持 vs 钓鱼 ...
- Web如何应对流量劫持?
虽然互联网经过多年的发展,可是网站使用的底层协议仍是 HTTP,HTTP 作为一种明文传播协议,所有的传输数据都是明文,我们都知道在通信中使用明文(不加密) 内容可能会被窃听,同时网站存在被劫持的风险 ...
- jQuery $(document).ready()和JavaScript onload事件
jQuery $(document).ready()和JavaScript onload事件 Why we need a right time? 对元素的操作和事件的绑定需要等待一个合适的时机,可以看 ...
- spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)转
关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用 ...
- jQuery的document ready与 onload事件——你真的思考过吗?
在进行实验和资料查询时,我遇到了几个关键问题: 1. window.onload到底是什么加载完触发? 2. body为什么会有onload事件? 3. 为什么是window.onload,而不是do ...
随机推荐
- js数据结构与算法——队列
<script> //创建一个队列 function Queue(){ let items = []; //向队尾添加一个新的项 this.enqueue = function(eleme ...
- OverFeat学习
[OverFeat Integrated Recognition,Localization and Detection using Convolutional Networks] Pierre Ser ...
- 五 Zabbix全网监控
监控的作用 我们的职责 1.保障企业数据的安全可靠. 2.为客户提供7*24小时服务. 3.不断提升用户的体验.在关键时刻,提前提醒我们服务器要出问题了当出问题之后,可以便于找到问题的根源 ...
- adb连接夜神模拟器执行命令
1.要进入夜神模拟器的bin目录 2.连接夜神模拟器 3.执行命令 cd %~dp0 nox_adb.exe connect 127.0.0.1>nul set num= :ok set /a ...
- 2018年最新JAVA面试题总结之基础(1)
转自于:https://zhuanlan.zhihu.com/p/39322967 1.JAVA中能创建volatile数组吗?volatile能使得一个非原子操作变成原子操作吗? 回答: 能,Jav ...
- 程序守护服务 Supervisor
一.什么是Supervisor? Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启.它是通过fork ...
- POSIX-Data Structure
struct sigevent The <signal.h> header shall define the sigeventstructure, which shall include ...
- 我的Python笔记04
摘要: 声明:本文整理借鉴金角大王的Python之路,Day4 - Python基础4 (new版) 本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件 ...
- Hive基础测试操作
一.Hive测试 1.查看数据库 show databases; 2.使用某个数据库,如默认数据库 user default; 3.创建表 create table if not exist itst ...
- ARTS Challenge- Week 1 (2019.03.25~2019.03.31)
1.Algorithm - at least one leetcode problem per week(Medium+) 986. Interval List Intersections https ...