跳到主要内容

3.1-同源策略

Create by fall on 03 Feb 2022 Recently revised in 29 Dec 2023

同源策略

Same-Origin Policy

同源策略:同域名,同端口号,同协议。它是浏览器最核心最基本的安全功能。

不同源的客户端脚本在没有明确授权的情况下是不允许读写其他网站的资源。

同源策略的限制

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM 无法获得。
  • AJAX 请求不能发送。

同源策略作用

  • 防止恶意网页可以获取其他网站的本地数据。
  • 防止恶意网站iframe其他网站的时候,获取数据。
  • 防止恶意网站在自已网站有访问其他网站的权利,以免通过cookie免登,拿到数据。

跨域

CORS(Cross-origin resource sharing)跨域资源共享

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。

JSONP

利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP 请求需要对方的服务器做支持才可以。

优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。

创建 JSONP:

  • 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
  • 创建一个 <script> 标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
  • 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')。
  • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP函数。

CORS

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

它由一系列传输的 HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。

请求和响应中的关于 CORS 的头文件

Access-Control-Allow-Origin:指出那个响应可以被共享

Access-Control-Allow-Headers

响应头

access-control-allow-origin: https://juejin.cn
access-control-allow-headers: content-type
access-control-max-age: 1800
access-control-allow-credentials: true
access-control-allow-methods: GET, OPTIONS, HEAD, PUT, POST

postMessage

跨域操作的window属性

postMessage 是 HTML5 XMLHttpRequest Level 2 中的 API,为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的iframe消息传递
  • 上面三个场景的跨域数据传递

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递

websocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生 WebSocket API 使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容

Node 中间件代理

Node 中间件代理,两次跨域

实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。代理服务器,需要做以下几个步骤:

  • 接受客户端请求。
  • 将请求转发给服务器。
  • 拿到服务器响应 数据。
  • 将响应转发给客户端。

nginx 反向代理

实现原理类似于Node中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。

使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

window.name + iframe

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

location.hash + iframe

实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现步骤:一开始 a.html 给 c.html 传一个 hash 值,然后 c.html 收到 hash 值后,再把 hash 值传递给b.html,最后b.html将结果放到 a.html 的 hash值中。 同样的,a.html和b.html是同域的,都是 http://localhost:3000,而c.html 是 http://localhost:4000

document.domain + iframe

该方式只能用于二级域名相同的情况下,比如 a.test.comb.test.com。 只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域。

实现原理:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。

参考文章

名称链接
MDNhttps://developer.mozilla.org/zh-CN/docs/Glossary/CORS