Fork me on GitHub
跨域通信

跨域通信

 

在工作的一段时间里,给我最大的感触就是:公司有非常多的子系统,而且子系统与子系统之间交互非常的频繁,虽然公司拥有封装了HttpClient及增加了路由功能的R系统(该系统目的就是满足现下系统与系统之间通信频繁的问题),但是大家都知道HttpClient只限于服务器端使用,当我们想用客户端脚本,比如Javascript来调用其他系统(不同域)的接口时,就会出现跨域通信的问题。那么为什么会出现跨域通信问题呢?当遇到跨域通信问题时,我们又有几种解决方案呢?另外,跨域通信又会引发哪些潜在的问题呢?下面我将慢慢解释这几个问题。

 

一、产生跨域通信问题的本质原因

  "无规矩不成方圆",这句话大家都知道,当我们在互联网的浪潮中冲浪的时候,我们无需考虑任何安全问题。因为互联网为了网民的安全,定制了很多策略(规矩),有一种策略叫同源策略,是每一种浏览器都必须实现并遵守的!那么同源策略到底限制了什么呢?

  同源策略规定:不同客户端脚本在没有明确授权的情况下,不能读写对方的资源(好比你不能碰别人的女朋友一样),这里有几个关键字很有必要解释一下。

  1、域、子域、同域

      所谓同域(源)指:当A域与B域具有相同的协议、域名、端口时称为同域(源)

  2、客户端脚本:由浏览器解释和执行的脚本语言,比如现下流行的Javascript,ActionScript,之前流行的VBScript   

  3、授权:服务端可以对客户端的访问进行授权,那么客户端也可以授权,比如HTML5中新标准中,当目标站点返回HTTP响应头Access-Control-Allow-Origin:http://www.cnblogs.com(A),那么浏览器就允许A站点去访问目标站点。   

  4、读写权限:Web上的资源很多,比如HTTP请求头的Referer只读,cookie即可写亦可读

  5、资源:资源是个广泛的概念,只要是数据都可以认为是资源。同源策略里的资源特指Web客户端的资源(HTTP头,DOM树..)

在这里特别要注意的是,同源策略虽然限制了客户端的资源,但对静态的资源文件是没有加以限制的,例如客户端脚本文件,样式CSS文件、图片,flash资源等静态文件。(<script src=""><img src=""> <link rel=""><iframe src="">)

二、为什么需要同源策略限制

   我们举例说明:比如一个黑客程序,他利用iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。如果没有该策略的限制,后果可想而知。

三、同源策略的应用场景

前面介绍了同源策略的规定其存在的价值,那么浏览器的同源策略究竟在哪些场景会起作用呢?下面的三种场景是比较常见的!

  1、窗口与窗口(不同域)之间的交互

  2、iframe嵌入了不同域的资源(浏览器并不限制iframe嵌入不同域的资源,但限制客户端脚本去访问)

  3、AJax应用(异步),请求不同域的资源

四、绕过同源策略解决跨域通信的方式

  互联网的发展催生了跨域通信的需求,各种跨域方法和协议满足了需求但也增加了各种风险(风险下面会讲)。尤其是现在mashup的盛行。那么怎么去绕过浏览器的同源策略呢?下面我会讲在实际应用中比较常用的绕过同源策略的方式,这些方式会分为:客户端技术和服务端技术。但这两种方式的本质就是利用同源策略的一个漏洞(同源策略虽然限制了客户端的资源,但对静态的资源文件是没有加以限制的)和浏览器中不能直接来跨域访问,而在服务器端没有跨域安全限制(可以在服务端完成跨域访问,而在客户端来取得结果)。客户端技术利用的是前者,而服务端技术利用的是后者。

  客户端技术

  1. iframe + document.domain(仅适用于子域之间,例如bbs.pclady.com.cn和www.pclady.com.cn) 
  2. iframe(其原理:在iframe加载新页面时,window.name的值是保持不变的,由此可以重定向iframe的引用地址,由外域转到本域) -> JQuery的window.name插件(封装了iframe的实现) -- 支持post请求
  3. 动态Script标签->Jsonp -> JQuery的$.getScript(弊端只支持get请求,没有差错控制机制)
  4. HTML5 postMessage(需要现代的浏览器才兼容,像之前的ie6不兼容)
  5. Flash + crossdomain跨域(需要用户安装flash,在互联网应用里用户体验不好)

1、动态Script标签,返回的数据格式必须是text/javascript(并非一定是JSON,可以是任何数据类型),跨域通信盛行之后,慢慢就产生了JSONP的概念(就是将动态插入Script标签的技术美称为JSONP,但网上却有一大堆关于JSONP的解释,简直是误导众生啊!)

复制代码
 1 <html> 
 2 <head> 
 3 <title>How Many Pictures Of Madonna Do We Have?</title> 
 4 <script type="text/javascript"> 
 5 // 回调函数
 6 function ws_results(obj) 
 7 { 
 8     // obj的格式可以是任意数据类型,比如JSON,字符串,xml
 9     // 我们只需要相应的解析即可
10     // 服务器端的返回格式一定得是这样  ws_results(obj);
11     alert(obj.ResultSet.totalResultsAvailable); 
12 } 
13  
14 function onClick() 
15 { 
16     var script = document.createElement("script"); 
17     script.type = "text/javascript"; 
18     script.src = "http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Madonna&output=json&callback=ws_results"; 
19     document.body.appendChild(script); 
20 } 
21 </script> 
22 </head> 
23 <body> 
24 <input type="button" value="click me!" onclick="onClick()"> 
25 </body> 
26 </html>
复制代码

2、Iframe+document.domain

复制代码
 1 /*
 2     对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,如果你异想天开的把script.a.com的domian设为alibaba.com那显然是会报错地!
 3 */代码如下:
 4 www.a.com上的a.html
 5 document.domain = 'a.com';
 6 var ifr = document.createElement('iframe');
 7 ifr.src = 'http://script.a.com/b.html';
 8 ifr.style.display = 'none';
 9 document.body.appendChild(ifr);
10 ifr.onload = function(){
11     var doc = ifr.contentDocument || ifr.contentWindow.document;
12     // 在这里操纵b.html
13     alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
14 };
15 
16 script.a.com上的b.html
17 document.domain = 'a.com';
18 /*
19 这种方式适用于{www.kuqin.com, kuqin.com, script.kuqin.com, css.kuqin.com}中的任何页面相互通信。
20 备注:某一页面的domain默认等于window.location.hostname。主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。 domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。
21 这种方式的缺陷:
22 1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
23 2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。
24 */
复制代码

3、Iframe->window.name 详情可查看http://www.cnblogs.com/rainman/archive/2011/02/21/1960044.html

复制代码
 1 <html>
 2 <head>
 3 <title>ddd</title>
 4 </head>
 5 <body>
 6 <script type="text/javascript">
 7 function check()
 8 {
 9     var btn = document.getElementById("test_submit");
10     var frm = document.forms["test_form"];
11     var ifm = document.getElementById("test_iframe");
12     frm.action = "http://xxx.xxx.xxx/post.php";
13     frm.target = "test_iframe";
14     frm.submit();
15     btn.disabled = "disabled";
16     ifm.onload = function(){
17         btn.disabled = "";
18         var str = ifm.contentWindow;
19         alert(str.document.body.innerHTML);
20         ifm.src = "about:blank"; 
21         ifm.onload = null; 
22     }     
23     return false; 
24 
25 }
26 
27 </script>
28 <form id="test_form" name="test_form" >
29     <input type="hidden" name="content" value="xxx" />
30     <input type="submit" name="test_submit" id="test_submit" />
31 </form>
32 <iframe id="test_iframe" name="test_iframe" width="1" height="1" style="display:none"></iframe>
33 </body>
34 </html>
35 
36 Iframe实现post跨域
复制代码

4、5由于用得不多,在这里就不详细解释了!详细可查看http://book.51cto.com/art/200903/113178.htm

  服务端技术

  1. 正向代理:与浏览器原理一样
  2. 反向代理:利用apache server或者nginx等Http服务器进行对url进行重写
  3. HttpClient: 模拟Http请求(通过HttpClient对其它域发出HTTP请求,只要不通过浏览器就不会有同源策略的限制)

在那么多处理方式中,当然每种方式都有它们的优缺点,当我们去选择这些方式的时候,我们应该以下面几个方面去考虑。

  1、自己是否可以操作其它域的服务端的资源

  2、请求的方式是get还是post

  3、跨域通信的方式(跨域通信有2种:本域和子域通信本域和其它域通信)

五、绕过同源策略引发的问题

  在避免同源策略时会向恶意用户露出攻击面,当恶意代码被插入 Web 应用程序中时当前的应用程序也易于受到攻击。遗憾的是,恶意代码进入 Web 应用程序的方法多种多样。使我们防不胜防。两种比较常见的的XSS(反射式与存储式)和CSRF,有兴趣的可以具体去了解一下!

六、其它同源策略

posted on 2013-12-12 22:46  HackerVirus  阅读(628)  评论(0编辑  收藏  举报