解锁跨域的九种姿势

2023-05-29 0 899

写在前面

针对责任编辑的八种方法我均写的有相应的demo模拟(相关联的后端文档,后端文档和命令行),雷西县不熟识的朋友都去试著一下。

互联网上存有很多相同的布吕马该文,我在自学的此时候基本上也是去看他们的该文,但是有些地方确实理解起来有点困难,所以责任编辑就这种造成了,希望能写一点儿和那时互联网上该文中都不一样的小东西。与此同时也把我他们的观点雷西县,和他们相互沟通交流他们的观点。

布吕马在以前一直煎熬着每个后端开发者。但那时,ssr服务器端图形的配置都须要介绍一点儿布吕马,介绍一点儿允诺的全权。

为什么存有布吕马

他们在解决的两个难题的与此同时他们应该先去介绍这个难题是如何造成的。

或许要使用布吕马,最终的元凶都是应用程序的相混思路,应用程序的是80路由器或者其他。这里也存有freenode的相同。想详细介绍请看 => MDN相关联用程序的相混思路的说明

尽管相混思路确实很无耻,但假如没相混思路使用者将会陷于很修为。比如,你正在吃着排骨哼着歌,逛着淘宝买小东西,但此时你的老师给你发了两个邮箱,接着你间接打开来看,倘若没相混思路,他在该中文网站时用两个iframe的条码接着把src对准淘宝网,此时没相混思路,他便能间接透过js操作方式iframe的网页.html时用iframe中填入b.html,你会辨认出当前2个网页是两个域的,你能在a中透过js控制b,和在b中间接用js操作方式没区别。但假如你填入的不是同两个域上面的网页,比如你填入的是淘宝,你会辨认出你不能透过js操作方式,你console.log(iframe.contentWindow)你会辨认出只有少数的两项。他们能去看看元老的该文:详解CSRF必杀技,尽管没永远牢固的盾,但有相混策略的存有,会让攻击的成本变得高一点儿。

尽管相混思路危害性很大,但他们还是在一定的情景上面须要进行布吕马处理,比如说腾讯全家人桶,你在腾讯世界地图和腾讯搜寻2者之间的确是放在2个域上面的(我没具体的去介绍,但我悖论的确是这种的)。在开发世界地图的此时候倘若须要应用搜寻的此时候就不得不用布吕马了。比如:腾讯搜寻,输出文字出现内容提示信息,假如我没判断错误就是采用的jsonp来做得布吕马。他们在自学了jsonp布吕马的此时候,

进入正题

1、JSONP

说起如何去解决布吕马,我相信每个人脑袋中跳出来的第两个词就是jsonp。因为应用程序的相混思路不限制script、link、img三个条码,比如他们经常用这三个

// 比如1.js 脚责任编辑件 say(haha);

我在html里面引入1.js文档,那么他讲会执行say函数,他们须要传入的数据就是haha。

所以jsonp的方法就是动态的创建两个script条码,接着设置src为他们须要进行布吕马的地址。当然这个方法须要后台的设置。他们能看我写的代码,

后端文档在

fontEndService/www/demo1/index,html btn.onclick = () => { jsonp(http://127.0.0.1:8888/api/getdata?jsonp=displayData); } function jsonp(url) { let script = document.createElement(script); script.setAttribute(src, url); document.getElementsByTagName(head)[0].appendChild(script); } function displayData(data) { msg.innerText = JSON.stringify(data); }

接着后端代码是用koa写的两个简约的接口,文档在service/app.js

// 简约的后端代码,他们直接调用后端传过来的须要执行的函数 router.get(/api/getdata, ctx => { const params = get_params(ctx.reqbody = `${params.jsonp || callback}(${JSON.stringify(data)})`; })

后端透过script条码给后台发送两个get允诺,在jsonp=displayData(两个他们接受到数据接着执行的方法,该方法是后端的),当我后台接受到允诺后,就返回两个,执行displayData这个方法的脚本。接着把他们须要传递的数据放在形参里面。这种就相当于他们在后端里面执行displayData这个方法。用这个方法来实现布吕马资源的共享。

此方法是比较常用的两个方法,简单粗暴。但此方法有两个致命的缺点就是只支持GET允诺。所以说假如后端网页仅

2、iframe+document.domainZ

这个方法,个人感觉不是特别的常用,因为这个布吕马方法要求2个域之间的主域名相同,子域相同,比如a.xxx.com和b.xxx.com。假如相同的话是不行的。

此方法的思想就是设置网页的document.domain把他们设置成相同的域名,比如都设置成xxx.com。这种来绕过相混思路。很简单的两个方法,具体的代码文档请看github。

代码里面的测试案列是,后端文档在7777路由器,后台文档在8888路由器,后端假如须要允诺后端的数据就存有布吕马,所以他们在后端8888路由器写两个提供数据的中转html,接着透过ajax或者其他的方法允诺到数据,接着把数据往外暴露。此方法须要2个html都须要设置相同的主域。

3、iframe+location.hash

这种方法是两个很奇妙的方法,尽管我感觉很鸡肋,但它的实现方法很巧妙,我在自学的此时候都感觉到不可思议,还能这么玩?

首先他们须要介绍hash是什么?

比如有两个这种的url:http://www.xxx.com#abc=123,那么他们透过执行location.hash就能得到这种的两个字符串#abc=123,与此同时改变hash网页是不会刷新的。这一点儿,相信很多自学三大框架的朋友应该都见识过hash路由。所以说他们能根据这一点儿来在#后面加上他们须要传递的数据。

加入那时他们有A网页在7777路由器(后端显示的文档),B网页在8888路由器,后台运行在8888路由器。他们在A网页中透过iframe嵌套B网页。

1、从A网页要传数据到B网页

他们在A网页中透过,修改iframe的src的方法来修改hash的内容。接着在B网页中添加setInterval事件来监听他们的hash是否改变,假如改变那么就执行相应的操作方式。比如像后台服务器提交数据或者上传图片这些。

2、从B网页传递数据到A网页

经过上面的方法,那么的确有聪明的朋友就在想那么,从B网页向A网页发送数据就是修改A网页的hash值了。对没错方法就是这种,但我在执行的此时候会出现一些难题。他们在B网页中间接:

parent.location.hash = “#xxxx”

这种是不行的,因为前面提到过的相混思路不能间接修改父级的hash值,所以这里采用了两个我认为很巧妙的方法。部分代码:

try { parent.location.hash = `message=${JSON.stringify(data)}`; } catch (e) { // ie、chrome 的安全机制无法修改parent.location.hash, // 利用两个中间html 的全权修改location.hash // 如A => B => C 其中,当前网页是B,AC在相同的两个域下。B 不能间接修改A 的 hash值故修改 C,让C 修改A // 文档地址: fontEndService/www/demo3/proxy.html if (!ifrProxy) { ifrProxy = document.createElement(iframe); ifrProxy.style.display = none; document.body.appendChild(ifrProxy); } ifrProxy.src = `http://127.0.0.1:7777/demo3/proxy.html#message=${JSON.stringify(data)}`; }

假如能间接修改他们就间接修改,假如不能间接修改,那么他们在B网页中再添加两个iframe接着对准C网页(他们暂时叫他全权网页,此网页和A网页是在相同的两个域上面),他们能用同样的方法在url后面他们须要传递的信息。在全权网页中:

parent.parent.location.hash = self.location.hash.substring(1);

只须要写这种的一段js代码就完成了修改A网页的hash值,同样在A网页中也添加两个setInterval事件来监听hash值的改变。

他们那时再来理一下思路。他们那时有三个网页,A,B,C。

A网页是他们后端显示的网页;

B网页他们能把他当做A网页也后端数据交互的两个中间网页,完成接受A的数据和向后台发送允诺。但由于相混思路的限制他们不能在B网页中间接修改A的hash值,所以他们须要借助两个与A网页在同两个域名下的C网页。

C网页他们把他当中两个全权网页,他们因为他和A页面在两个域下,所以能修改A的hash值。所以B网页修改C网页的hash值,接着C网页修改A网页的hash值。(C就是两个打工的)

此方法尽管我个人感觉实现的思路很巧妙但,使用价值似乎不高,因为他实现的核心思路就是透过修改URL的hash值,接着用定时器来监听值的改变来修改。所以说最大的难题就是,他们传递的数据会间接在URL里面显示出来,不是很安全,与此同时URL的长度是一定的所以传输的数据也是有限的。

4、iframe+window.name

相比于前面2种iframe的方法,这种方法的使用人数要多一点儿。因为他有效的解决了前面2种方法很大的缺点。这种方法的原理就是window.name属性在于加载不同的网页(包括域名相同的情况下),假如name值没修改,那么它将不会变化。并且这个值能非常的长(2MB)

A网页中部分代码:

let mark = false; let ifr = document.createElement(iframe); ifr.src = “http://127.0.0.1:8888/demo4”; ifr.style.display = none; document.body.appendChild(ifr); ifr.onload = () => { // iframe 中数据加载完成,触发onload事件 if (mark) { msg.innerText = ifr.contentWindow.name;// 这就是数据 document.body.removeChild(ifr); mark = false; } else { mark = true; // 修改src对准本域的两个网页(这个网页什么都没) ifr.contentWindow.location = “http://127.0.0.1:7777/demo4/proxy.html”; } }

5、postMessage

postMessage是HTML5引入的API。他能

A网页中部分代码:

<iframe id=”iframe” src=”http://127.0.0.1:8888/demo5″ frameborder=”0″></iframe> iframe.contentWindow.postMessage(getData, http://127.0.0.1:8888); // 监听其他网页给我发送的数据 window.addEventListener(message, e => { if (e.origin !== http://127.0.0.1:8888) return; msg.innerText = e.data; })

这里他们给目标窗口127.0.0.1:8888推送了getData的数据。接着在B网页中添加事件的监听。

B网页中部分代码:

window.addEventListener(rn; const data = e.data; if (data === getData) { // 根据接受到的数据,来进行下一步的操作方式 myAjax(/api/getdata, notice); } }) function notice(data) { // 向后台允诺到数据以后,在向父级推送消息 top.postMessage(JSON.stringify(data), http://127.0.0.1:7777) }

我个人认为这种方式就是两个事件的发布与监听,至少说能无视相混思路。

6、cors

其实对于布吕马资源的允诺,应用程序已经把他们的允诺发放给了服务器,应用程序也接受到了服务器的响应,只是应用程序一看他们2个的域不一样就把消

在后台代码中我以KOA列子

const Koa = require(koa); const router = require(koa-router)(); // 引入中间件 const cors = require(koa2-cors); const app = new Koa(); // 根据后台服务器的类型,打开布吕马设置 // cors安全风险很高,所以,实际线上的配置肯定要比这个更加复杂,须要根据他们的需求来做 app.use(cors()); router.get(/api/getdata, ctx => { ctx.body = { code: 200, msg: 我是配置有cors的服务器传输的数据 } }) app.use(router.routes(), router.allowedMethods()); console.log(配置有cors的服务器地址在:http://127.0.0.1:8889); app.listen(8889);

配置了CORS的应用程序请求响应信息

解锁跨域的九种姿势

布吕马允诺响应信息

解锁跨域的九种姿势

7、NGINX

采用nginx做全权应该是目前布吕马解决方案最好的一种。那时强调前后端分离,后端根据后端提供的接口进行数据的交互接着图形网页。在后端三大框架的与此同时,开发过程中不需要他们针对布吕马配置很多。在网页上线以后。他们经常采用nginx来加载静态的资源,他们把他们后端打包好的文档放到nginx的目录上面,让nginx来处理客服端的静态资源的允诺。接着后端部署到另外两个路由器号上面,当他们须要进行数据的交互的此时候,透过nginx全权把后端数据全权到后端网页。这种的步骤是相较于传统的布吕马是最简单也是最有效的一种方法,因为nginx又没相混思路。不用考虑什么兼容性也不用考虑数据大小。他们在服务器(或者测试代码的此时候在本地)安装nginx服务,接着找到他们nginx的命令行,添加以下命令行:

server { # 把网页部署的路由器 listen 8080; # 静态网页存放的目录 root /var/www/html; index index.html index.htm index.php; # 只全权 /api 开头的接口,其他接口不全权 location /api/ { # 须要全权的地址, 输出他们的后台api地址 proxy_pass http://127.0.0.1:8888; } }

这种,他们能全权相同url开头的允诺到相同的后端进行处理,对以后服务器做负载均衡和反向全权也很简单。

8、nodejs

其实这种办法和上一种用nginx的方法是差不多的。都是你把允诺发给两个中间人,由于中间人没相混思路,他能间接全权或者透过爬虫或者其他的手段得到想到的数据,接着返回(是不是和VPN的原理有点类似)。

当然那时常见的就是用nodejs作为数据的中间件,同样,相同的语言有相同的方法,但本质是一样的。我上次他们同构他们的博客网页,用react服务器端图形,因为应用程序的相混思路,允诺不到数据,接着就用了nodejs作为中间件来全权允诺数据。

部分代码:

const Koa = require(koa); // 全权 const Proxy = require(koa-proxy); // 对以前的异步函数进行转换 const Convert = require(koa-convert); const app = new Koa(); const server = require(koa-static); app.use(server(__dirname+”/www/”,{ extensions: [html]})); app.use(Convert(Proxy({ // 须要全权的接口地址 host: http://127.0.0.1:8888, // 只全权/api/开头的url match: /^\/api\// }))); console.log(服务运行在:http://127.0.0.1:7777); app.listen(7777);

是不是和nginx很类似呀!!

9、webSocket

webSocket他们应该都有所耳闻,主要是为了客服端和服务器端进行全双工的通信。但这种通信是能进行跨路由器的。所以说他们能用这个漏洞来进行布吕马数据的交互。

我个人认为,这种方法实质上和postMessage差不多,但不是特别的常用吧!(仅仅个人观点)

所以说他们能很轻松的构建基于webSocket构建两个客服端和服务器端。代码在github建议他们都多多去运行一下,介绍清楚。这里就不贴了。

解锁跨域的九种姿势

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务