综述
出于防范跨站脚本攻击的同源安全策略,浏览器禁止客户端脚本(如Javascript)对不同域名的服务进行跨域调用。
同源策略(Same Origin)中的源有着严格的定义,参见RFC6454,第4章节。一般而言,Origin由{protocol,host,port}三部分组成。
下面是同源检查的一些实例:
可能有点意外的是,一般我们会认为不同的子域名应该被当做同域名,是安全的调用,但实际上浏览器同源策略甚至禁止了不同子域名和端口的服务之间的调用。
解决方法
有时候上述限制过于严格,为了在两个不同Origin的网页(服务)之间进行通讯,我们可以采用如下的一些技术方法:
1、服务代理模式
即在web服务器上封装第三方服务,然后给自己同源的web页面调用。
2、跨子域名的调用
如果想从www.example.com调用api.example.com的html/xml数据服务,那么可以通过使用iframe,并在document和iframe中均设置相同的document.domain属性,那么document和iframe所对应的页面直接就可以通信而不会有任何安全冲突(security violation),注意必须设置相同的一级域名(document.domain="example.com"),不要设置子域名。代码示例如下:
t.html
<html> <head> <script type="text/javascript"> document.domain = "example.com"; function update_me(result) { document.getElementById('update-me').innerHTML = "Result: " + result; } function load() { var c = document.createElement('iframe'); c.style.display = "none"; c.src = "http://api.example.com/cross-subdomains-ajax/t-frame.html#" + document.domain; document.body.appendChild(c); } </script> </head> <body onload="load();"> <div id="update-me"></div> </body> </html>
t-frame.html
<html> <head> <script type="text/javascript"> function spoof(status,headers,result) { var old_domain = document.domain; document.domain = example.com; window.parent.update_me("Ok,got it from the frame !"); try { document.domain = old_domain; } catch (e) { } } spoof(); </script> </head> </html>
3、跨源资源共享
这是已经标准化的技术方法,参见W3规范文档Cross OriginResouceSharing:http://www.w3.org/TR/cors/
通过在服务端设置Access-Control-Allow-Origin
响应头(一般回填$_SERVER['HTTP_ORIGIN']),
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
来告诉浏览器该服务允许来自特定源的访问或者允许所有人访问。
尽管该规范支持使用origin list的形式,但实际浏览器实现只支持了单个origin、*、null,如果要配置多个访问源,可以在代码中处理如下(PHP):
$allowed_origins = array( "http://www.example.com","http://app.example.com","http://cms.example.com",); if (in_array($_SERVER['HTTP_ORIGIN'],$allowed_origins)){ @header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']); }
或者在httpd配置或.htaccess文件(如果使用的是Apache服务器)中添加如下语句:
SetEnvIf Origin "^(.*\.example\.com)$" ORIGIN_SUB_DOMAIN=$1 Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
如用的是Nginx,配置类似如下:
location / { # this will echo back the origin header if ($http_origin ~ "example.com$") { add_header "Access-Control-Allow-Origin" $http_origin; } }
4、使用JSONP协议
出于同源策略,HTML禁止在两个页面之间进行通讯,但<script>元素是个例外,JSONP利用这个开放特性来实现跨域调用,
在客户端调用提供JSONP支持的URL Service,获取JSONP格式数据,然后在callback函数中处理返回的json协议数据:
<script type="text/javascript" src="http://api.example.com/getdata?jsonp=callback"></script>
$.ajax({ dataType: 'jsonp',data: 'id=10',jsonp: 'jsonp_callback',url: 'http://api.example.com/getdata',success: function () { // do stuff },});
5、跨文档消息(Cross-documentMessaging)
这个是HTML5引入的一个在跨域页面之间通讯的方法,参见W3规范 http://dev.w3.org/html5/postmsg/
postMessage API应用范围主要有2个,1是文档和内嵌frame之间的消息通信,2是文档和自身通过脚本打开的windows之间的消息通信
by iefreer