一文梳理同源策略与跨域技术

1.同源策略

同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

1.1何谓同源

如果两个 URL 的 protocolport (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。这个方案也被称为“协议/主机/端口元组”,或者直接是 “元组”。

下表给出了与 URL http://store.company.com/dir/page.html 的源进行对比的示例:

URL 结果 原因
http://store.company.com/dir2/other.html 同源 只有路径不同
http://store.company.com/dir/inner/another.html 同源 只有路径不同
https://store.company.com/secure.html 失败 协议不同
http://store.company.com:81/dir/etc.html 失败 端口不同 ( http:// 默认端口是80)
http://news.company.com/dir/other.html 失败 主机不同

随着互联网的发展,"同源政策"越来越严格。目前,如果非同源,共有三种行为受到限制。

(1) Cookie、LocalStorage 和 IndexDB 无法读取。

(2) DOM 无法获得。

(3) AJAX 请求不能发送。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。

2.跨域

2.1 何谓跨域?

跨域问题是由于浏览器为了防止CSRF(跨站请求伪造)攻击,避免恶意攻击而带来的风险而采取的同源策略限制。当一个页面中使用XMLHTTPRequest对象发送HTTP请求时(XHR请求),必须保证当前页面和请求的对象是同源的

能实现跨域的技术还是比较多的,篇幅有限,这篇文章主要给大家带来:

  • JSONP跨域
  • CORS跨域
  • postmessage
  • 服务器代理
  • WebSocket

2.2 JSONP跨域

JSONP的原理:静态资源请求不受同源策略影响。具体:浏览器只对XHR(XMLHttpRequest)请求有同源请求限制,而对script标签src属性、link标签ref属性和img标签src属性没有这种限制,利用这个“漏洞”就可以很好的解决跨域请求.

JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调
函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。

<script>
    function jsoncallback(response){
        console.log(response);
    }
    var script = document.createElement("script");
    script.src = "https://me.kervi.cn/v1/datas/jsondata.php?callback=jsoncallback";
    document.body.insertBefore(script, document.body.firstChild);
</script>

JSONP 是通过动态<script> 元素来使用的,使用时可以为src 属性指定一个跨域 URL。

总结:

  • 简单易用
  • 能够直接访问响应文本,支持在浏览器与服务器之间双向通信
  • 仅支持GET请求(通过script标签的的src属性发送);
  • 需要后端配合,前后端约定一个字段名,这里约定的jsoncallback,来传递一个函数名,从而使得前端可以使用对应的callback函数,拿到数据,处理数据。
  • JSONP 是从其他域中加载代码执行。如果其他域不安全,很可能会在响应中夹带一些恶意代码,而此时除了完全放弃 JSONP 调用之外,没有办法追究。因此在使用不是你自己运维的 Web 服务时,一定得保证它安全可靠;
  • 要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 <script> 元素新增了一个 onerror事件处理程序,但目前还没有得到任何浏览器支持。为此,开发人员不得不使用计时器检测指定时间内是否接收到了响应。但就算这样也不能尽如人意,毕竟不是每个用户上网的速度和带宽都一样。

2.3 CORS跨域

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

在各种服务端代码实现如下:

// 根据不同语言规则,具体语法有所不同,此处以NodeJs的express为例
//设置跨域访问  
app.all('*', function(req, res, next) {  
    res.header("Access-Control-Allow-Origin", "*");  
    res.header("Access-Control-Allow-Headers", "X-Requested-With");  
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    next();  
});   

Nginx实现如下:

server {
    ... 
    add_header Access-Control-Allow-Credentials true;
    add_header Access-Control-Allow-Origin $http_origin;
        
    location /file {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin $http_origin;
            add_header Access-Control-Allow-Methods $http_access_control_request_method;
            add_header Access-Control-Allow-Credentials true;
            add_header Access-Control-Allow-Headers $http_access_control_request_headers;
            add_header Access-Control-Max-Age 1728000;
            return 204;
        }   
    }
    ...
}

2.4 postMessage

「window.postMessage()」 方法可以安全地实现跨源通信。可以允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本文档,多窗口,跨域消息传递.多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案.

兼容性:

语法

otherWindow.postMessage(message, targetOrigin, [transfer])

  • message 你要发送的信息(字符串和对象都可以)
  • targetOrigin 你要发送信息的目标域名
  • transfer 可选参数,与消息一起传输的Transferable对象序列。这些对象的所有权将提供给目标端,并且它们在发送端不再可用。

已经有人写了不错的实践,博主这里就不做介绍,可以阅读postmessage可真太有用了了解。

总结

  • postMessage是html5新增的一个解决跨域的方法,主要解决不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递,多用于页面与嵌套的iframe消息传递

  • 由于默认允许跨域,所以导致一些安全问题,主要攻击方式有两种,一是伪造数据发送端,易造成XSS,二是伪造数据获取端,类似JSONP劫持。

  • 用于接收消息的任何事件侦听器必须首先使用origin和可能的source属性检查消息发送者的身份。未能检查origin和可能的source属性可以实现跨站点脚本攻击。

2.5 websocket

WebSocket 是一种网络通信协议,可在网络浏览器和服务器之间建立“套接字”连接。这种方式本质没有使用了 HTTP 的响应头, 因此也没有跨域的限制。

前端部分(代码来自秋风的笔记-10种跨域解决方案(附终极大招)

<script>  let socket = new WebSocket("ws://localhost:8080");  socket.onopen = function() {    socket.send("秋风的笔记");  };  socket.onmessage = function(e) {    console.log(e.data);  };</script>复制代码

后端部分

const WebSocket = require("ws");const server = new WebSocket.Server({ port: 8080 });server.on("connection", function(socket) {  socket.on("message", function(data) {    socket.send(data);  });});

总结

  • 建立在 TCP 协议之上,服务器端的实现比较容易。
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  • 数据格式比较轻量,性能开销小,通信高效。
  • 可以发送文本,也可以发送二进制数据。
  • 没有同源限制,客户端可以与任意服务器通信。
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

2.6 Nginx反向代理

这里需要说明的是: 跨域是浏览器行为,不是服务器行为。同源策略仅是针对浏览器的安全策略。服务器端调用HTTP接口只是使用HTTP协议,不需要同源策略,也就不存在跨域问题。

实际上,请求已经到达服务器了,只不过在回来的时候被浏览器限制了

实现思路:通过Nginx配置一个代理服务器域名与发出请求域名相同,端口不同,反向代理访问目标域名。

背景:domain1需要跨域访问domain2

server {
        listen 80;
        server_name www.domain1.com;
        location / {
            proxy_pass www.domain2.com:8080;
        }
}

需要说明的是,前端跨域的技术还有许多,只是限于篇幅没有讲到,比如node正向代理,window.name+iframe等。还想了解别的跨域方案的小伙伴可以看本文的参考文章去观摩观摩。

小结

同源策略仅仅是针对浏览器的安全策略,跨域是浏览器行为,如果两个 URL 的 protocolport (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。现在有许多的跨域方案可供大家选择,本文仅仅选择了JSONP、CORS、postMessage等6种跨域技术进行了阐述,希望能在梳理自己知识点的同时帮助到一些人。

参考文章:

jsonp完美解决跨域问题,简单

跨域解决方案实践cors及jsonp

跨资源共享cors详解-阮一峰

浏览器同源政策及其规避方法-阮一峰

postmessage滥用导致的安全问题

10种跨域解决方案(附终极大招)

websocket教程-阮一峰

9种常见的前端跨域解决方案

posted @ 2020-07-05 20:26  川南烟雨  阅读(290)  评论(0编辑  收藏  举报