同源策略和跨域访问

栏目: Json · 发布时间: 7年前

内容简介:同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。同源:URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。同源策略:浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。从一个域上加载的脚本不允许访问另外一个域的文档属性。

一:什么是同源策略

同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。

同源:URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。

同源策略:浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。从一个域上加载的脚本不允许访问另外一个域的文档属性。

在浏览器中,<script>、<img>、<iframe>、<link>等标签都可以加载跨域资源,而不受同源限制,但浏览器限制了JavaScript的权限使其不能读、写加载的内容。

另外同源策略只对网页的HTML文档做了限制,对加载的其他静态资源如javascript、css、图片等仍然认为属于同源。

二:Ajax跨域

Ajax (XMLHttpRequest)请求受到同源策略的限制。Ajax通过XMLHttpRequest能够与远程的服务器进行信息交互,另外XMLHttpRequest是一个纯粹的Javascript对象,这样的交互过程,是在后台进行的,用户不易察觉。因此,XMLHTTP实际上已经突破了原有的Javascript的安全限制。

举个例子:

假设某网站引用了其它站点的javascript,这个站点被compromise并在javascript中加入获取用户输入并通过ajax提交给其他站点,这样就可以源源不断收集信息。

或者某网站因为存在漏洞导致XSS注入了javascript脚本,这个脚本就可以通过ajax获取用户信息并通过ajax提交给其他站点,这样就可以源源不断收集信息。

如果我们又想利用XMLHTTP的无刷新异步交互能力,又不愿意公然突破Javascript的安全策略,可以选择的方案就是给XMLHTTP加上严格的同源限制。

这样的安全策略,很类似于Applet的安全策略。IFrame的限制还仅仅是不能访问跨域HTMLDOM中的数据,而XMLHTTP则根本上限制了跨域请求的提交。(实际上下面提到了CORS已经放宽了限制)

随着Ajax技术和网络服务的发展,对跨域的要求也越来越强烈。下面介绍Ajax的跨域技术。

1、JSONP

JSONP技术实际和Ajax没有关系。我们知道<script>标签可以加载跨域的javascript脚本,并且被加载的脚本和当前文档属于同一个域。因此在文档中可以调用/访问脚本中的数据和函数。如果javascript脚本中的数据是动态生成的,那么只要在文档中动态创建<script>标签就可以实现和服务端的数据交互。

JSONP就是利用<script>标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。其中callback函数本地文档的JavaScript函数,服务器端动态生成的脚本会产生数据,并在代码中以产生的数据为参数调用callback函数。当这段脚本加载到本地文档时,callback函数就被调用。

原理:通过script标签引入的js不收同源策略的限制,而XmlHttpRequest 对象受到同源策略的影响,可以加载跨域服务器上的脚本,用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。

jsonp包含两个参数:回调函数和数据。回调函数是当响应到来时要放在当前页面被调用的函数。数据就是传入回调函数中的json数据,也就是回调函数的参数了。

a.使用 jQuery 集成的 $.ajax 实现 JSONP 跨域调用

下面的例子,我们将 服务器 3000 上的请求页面的 JavaScript 代码为:

1 // 回调函数
 2 function jsonpCallback(data) {
 3     console.log("jsonpCallback: " + data.name)
 4 }
 5 $("#submit").click(function() {
 6     var data = {
 7         name: $("#name").val(),
 8         id: $("#id").val()
 9     };
10     $.ajax({
11         url: 'http://localhost:3001/ajax/deal',
12         data: data,
13         dataType: 'jsonp',
14         cache: false,
15         timeout: 5000,
16         // jsonp 字段含义为服务器通过什么字段获取回调函数的名称
17         jsonp: 'callback',
18         // 声明本地回调函数的名称,jquery 默认随机生成一个函数名称
19         jsonpCallback: 'jsonpCallback',
20         success: function(data) {
21             console.log("ajax success callback: " + data.name)
22         },
23         error: function(jqXHR, textStatus, errorThrown) {
24             console.log(textStatus + ' ' + errorThrown);
25         }
26     });
27 });

服务器 3001 上对应的处理函数为:

1 app.get('/ajax/deal', function(req, res) {
2     console.log("server accept: ", req.query.name, req.query.id)
3     var data = "{" + "name:'" + req.query.name + " - server 3001 process'," + "id:'" + req.query.id + " - server 3001 process'" + "}"
4     var callback = req.query.callback  //获得请求端回调函数
5     var jsonp = callback + '(' + data + ')'
6     console.log(jsonp)
7     res.send(jsonp)
8     res.end()
9 })

这里一定要注意 data 中字符串拼接,不能直接将 JSON 格式的 data 直接传给回调函数,否则会发生编译错误: parsererror Error: jsonpCallback was not called

b.使用 <script> 标签原生实现 JSONP

经过上面的事件,你是不是觉得 JSONP 的实现和 Ajax 大同小异?

其实,由于实现的原理不同,由 JSONP 实现的跨域调用不是通过 XmlHttpRequset 对象,而是通过 script 标签,所以在实现原理上,JSONP 和 Ajax 已经一点关系都没有了。看上去形式相似只是由于 jQuery 对 JSONP 做了封装和转换。

比如在上面的例子中,我们假设要传输的数据 data 格式如下:

{
    name: "chiaki",
    id": "3001"
}

那么数据是如何传输的呢? HTTP 请求头的第一行如下:

GET /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1

可见,即使形式上是用 POST 传输一个 JSON 格式的数据,其实发送请求时还是转换成 GET 请求。

其实如果理解 JSONP 的原理的话就不难理解为什么 只能使用 GET 请求方法 了。由于是通过 script 标签进行请求,所以上述传输过程根本上是以下的形式:

<script src = 'http://localhost:3001/ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032'></script>

这样从服务器返回的代码就可以直接在这个 script 标签中运行了。下面我们自己实现一个 JSONP:

服务器 3000请求页面的 JavaScript 代码中,只有回调函数 jsonpCallback:

function jsonpCallback(data) {
    console.log("jsonpCallback: "+data.name)
}

服务器 3000请求页面还包含一个 script 标签:

<script src = 'http://localhost:3001/jsonServerResponse?jsonp=jsonpCallback'></script>

服务器 3001上对应的处理函数:

app.get('/jsonServerResponse', function(req, res) {
      var cb = req.query.jsonp //这里得到请求页面的回调函数
      console.log(cb)
        //思考一下为什么这里要这样写
      var data = 'var data = {' + 'name: $("#name").val() + " - server 3001 jsonp process",' + 'id: $("#id").val() + " - server 3001 jsonp process"' + '};'
      var debug = 'console.log(data);' //打印var data="";
      var callback = '$("#submit").click(function() {' + data + cb + '(data);' + debug + '});' 
      res.send(callback) //返回的是一个点击按钮的事件  
      res.end()
 })

与上面一样,我们在所获取的参数后面加上 “ - server 3001 jsonp process” 代表服务器对数据的操作。从代码中我么可以看到,处理函数除了根据参数做相应的处理,更多的也是进行字符串的拼接。

JSONP不足:

script

2、 使用 CORS 实现跨域调用

原理:CORS的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 请求 CORS 一般用XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。

3、 window.name

window 对象的name属性是一个很特别的属性,当该window的location变化,然后重新加载,它的name属性可以依然保持不变。那么我们可以在页面 A中用iframe加载其他域的页面B,而页面B中用JavaScript把需要传递的数据赋值给window.name,iframe加载完成之后(iframe.onload),页面A修改iframe的地址,将其变成同域的一个地址,然后就可以读出iframe的window.name的值了(因为A中的window.name和iframe中的window.name互相独立的,所以不能直接在A中获取window.name,而要通过iframe获取其window.name)。这个方式非常适合单向的数据请求,而且协议简单、安全。不会像JSONP那样不做限制地执行外部脚本。

4、Proxy服务器代理

在数据提供方没有提供对JSONP协议或者window.name协议的支持,也没有对其它域开放访问权限,我们可以通过server proxy的方式来抓取数据。例如当baidu.com域下的页面需要请求google.com下的资源文件getUsers.php时,直接发送一个指向google.com/getUsers.php的Ajax请求肯定是会被浏览器阻止。这时,我们在Baidu.com下配一个代理,然后把Ajax请求绑定到这个代理路径下,例如baidu.com/proxy/,然后这个代理发送HTTP请求访问google.com下的getUsers.php,跨域的HTTP请求是在服务器端进行的(服务器端没有同源策略的限制),客户端并没有产生跨域的Ajax请求。这个跨域方式下不需要和目标资源签订协议,带有侵略性。

5、 flash URLLoader

flash有自己的一套安全策略,服务器可以通过crossdomain.xml文件来声明能被哪些域的SWF文件访问,SWF也可以通过API来确定自身能被哪些域的SWF加载。当跨域访问资源时,例如从域baidu.com请求域google.com上的数据,我们可以借助flash来发送HTTP请求。首先,修改域google.com上的crossdomain.xml(一般存放在根目录,如果没有需要手动创建) ,把baidu.com加入到白名单。其次,通过Flash URLLoader发送HTTP请求,最后,通过Flash API把响应结果传递给JavaScript。Flash URLLoader是一种很普遍的跨域解决方案,不过需要支持iOS的话,这个方案就不可行了。

6、 document.domain(两个iframe之间)

通过修改document的domain属性,我们可以在域和子域或者不同的子域之间通信。同域策略认为域和子域隶属于不同的域,比如baidu.com和 youxi.baidu.com是不同的域,这时,我们无法在baidu.com下的页面中调用youxi.baidu.com中定义的JavaScript方法。但是当我们把它们document的domain属性都修改为baidu.com,浏览器就会认为它们处于同一个域下,那么我们就可以互相获取对方数据或者操作对方DOM了。

问题:

1、安全性,当一个站点被攻击后,另一个站点会引起安全漏洞。

2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。

7、 使用HTML5的postMessage方法(两个iframe之间或者两个页面之间)

高级浏览器Internet Explorer 8+, chrome,Firefox , Opera 和 Safari 都将支持这个功能。这个功能主要包括接受信息的"message"事件和发送消息的"postMessage"方法。比如baidu.com域的A页面通过iframe嵌入了一个google.com域的B页面,可以通过以下方法实现A和B的通信

A页面通过postMessage方法发送消息:

window.onload = function() {  
    var ifr = document.getElementById('ifr');  
    var targetOrigin = "http://www.google.com";  
    ifr.contentWindow.postMessage('hello world!', targetOrigin);  
};

postMessage的使用方法:

otherWindow.postMessage(message, targetOrigin);

otherWindow: 指目标窗口,也就是给哪个window发消息,是 window.frames 属性的成员或者由 window.open 方法创建的窗口

message: 是要发送的消息,类型为 String、Object (IE8、9 不支持)

targetOrigin: 是限定消息接收范围,不限制请使用 '*'

B页面通过message事件监听并接受消息:

var onmessage = function (event) {  
  var data = event.data;//消息  
  var origin = event.origin;//消息来源地址  
  var source = event.source;//源Window对象  
  if(origin=="http://www.baidu.com"){  
console.log(data);//hello world!  
  }  
};  
if (typeof window.addEventListener != 'undefined') {  
  window.addEventListener('message', onmessage, false);  
} else if (typeof window.attachEvent != 'undefined') {  
  //for ie  
  window.attachEvent('onmessage', onmessage);  
}

三:Cookie 同源策略

Cookie中的同源只关注域名,忽略协议和端口。所以https://localhost:8080/和http://localhost:8081/的Cookie是共享的。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning Processing

Learning Processing

Daniel Shiffman / Morgan Kaufmann / 2008-08-15 / USD 49.95

Book Description Teaches graphic artists the fundamentals of computer programming within a visual playground! Product Description This book introduces programming concepts in the context of c......一起来看看 《Learning Processing》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具