【网络安全】浅析跨域原理及如何实现跨域
前言
我们在解决一个问题的时候应该先去了解这个问题是如何产生的,为什么会有跨域的存在呢?其实,最终的罪魁祸首都是浏览器的同源策略,浏览器的同源策略限制我们只能在相同的协议、IP 地址、端口号相同,如果有任何一个不通,都不能相互的获取数据。并且,http 和 https 之间也存在跨域,因为 https 一般采用的是 443 端口,http 采用的是 80 端口或者其他。
同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 XSS、CSRF 等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。
同源策略限制内容有:
1.Cookie、LocalStorage、IndexedDB 等存储性内容
2.DOM 节点
3.AJAX 请求发送后,结果被浏览器拦截了
但是有三个标签是允许跨域加载资源:
<img src=XXX>
<link href=XXX>
<script src=XXX>
常见的跨域场景
需要网络安全资料的朋友可以私信回复“资料”获取【点击查看】
当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示:
其实,跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了,常见的 9 种跨域方案,主要有: 通过 jsonp 跨域、 document.domain + iframe 跨域、 location.hash + iframe、window.name + iframe 跨域、postMessage 跨域、跨域资源共享(CORS)、nginx 代理跨域、nodejs 中间件代理跨域、WebSocket 协议跨域 9 种,并有着各自独特的跨域原理。
一:JSONP 实现跨域
原理:jsonp 实现跨域的原理是跨域的服务端把客户端所需要的数据放进客户端本地的一个 js 方法里,进行调用,客户端在本地的 js 对返回的数据进行处理。这样就实现了不同域名下的两个站点间的交流。
JSONP 优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。
JSONP 的实现流程:
声明一个回调函数,其函数名(如 show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的 data)。
创建一个<script>标签,把那个跨域的 API 数据接口地址,赋值给 script 的 src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是 show,它准备好的数据是 show('我不爱你')。
最后服务器把准备的数据通过 HTTP 协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。
二:iframe+document.domain 跨域
此方法的思想就是设置页面的 document.domain 把他们设置成相同的域名,比如都设置成 xxx.com,这样来绕过同源策略。
代码里面的测试案列是,前端文件在 7777 端口,后台文件在 8888 端口,前端如果需要请求后端的数据就存在跨域,所以我们在后端 8888 端口写一个提供数据的中转 html,然后通过 ajax 或者其他的方法请求到数据,然后把数据往外暴露,此方法需要 2 个 html 都需要设置相同的主域。
三:location.hash+iframe 跨域
这种方法是一个很奇妙的方法,比如有一个这样的 url:http://www.xxx.com#abc=123,那么我们通过执行 location.hash 就可以得到这样的一个字符串 #abc=123,同时改变 hash 页面是不会刷新的。
假如现在我们有 A 页面在 7777 端口(前端显示的文件),B 页面在 8888 端口,后台运行在 8888 端口。我们在 A 页面中通过 iframe 嵌套 B 页面。
从 A 页面要传数据到 B 页面
我们在 A 页面中通过,修改 iframe 的 src 的方法来修改 hash 的内容。然后在 B 页面中添加 setInterval 事件来监听我们的 hash 是否改变,如果改变那么就执行相应的操作。比如像后台服务器提交数据或者上传图片这些。
从 B 页面传递数据到 A 页面
经过上面的方法,那么肯定有聪明的朋友就在想那么,从 B 页面向 A 页面发送数据就是修改 A 页面的 hash 值了。对没错方法就是这样,但是我在执行的时候会出现一些问题。我们在 B 页面中直接:parent.location.hash = "#xxxx"
这样是不行的,因为前面提到过的同源策略不能直接修改父级的 hash 值,所以这里采用了一个我认为很巧妙的方法。部分代码:
如果可以直接修改我们就直接修改,如果不能直接修改,那么我们在 B 页面中再添加一个 iframe 然后指向 C 页面(我们暂时叫他代理页面,此页面和 A 页面是在相同的一个域下面),我们可以用同样的方法在 url 后面我们需要传递的信息。在代理页面中:parent.parent.location.hash = self.location.hash.substring(1);
只需要写这样的一段 js 代码就完成了修改 A 页面的 hash 值,同样在 A 页面中也添加一个 setInterval 事件来监听 hash 值的改变。
实现的核心思路就是通过修改 URL 的 hash 值,然后用定时器来监听值的改变来修改。所以说最大的问题就是,我们传递的数据会直接在 URL 里面显示出来,不是很安全,同时 URL 的长度是一定的所以传输的数据也是有限的。
四:window.name+iframe 跨域
原理就是 window.name 属性在于加载不同的页面(包括域名不同的情况下),如果 name 值没有修改,那么它将不会变化,并且这个值可以非常的长(2MB)
方法原理:A 页面通过 iframe 加载 B 页面。B 页面获取完数据后,把数据赋值给 window.name。然后在 A 页面中修改 iframe 使他指向本域的一个页面。这样在 A 页面中就可以直接通过 iframe.contentWindow.name 获取到 B 页面中获取到的数据。
五:postMessage 跨域
postMessage 是 HTML5 引入的API。他可以解决多个窗口之间的通信(包括域名的不同)。我个人认为他算是一种消息的推送,可以给每个窗口推送。然后在目标窗口添加 message 的监听事件。从而获取推送过来的数据。
六:跨域资源共享(CORS)
其实对于跨域资源的请求,浏览器已经把我们的请求发放给了服务器,浏览器也接受到了服务器的响应,只是浏览器一看我们 2 个的域不一样就把消息给拦截了,不给我们显示。所以我们如果我们在服务器就告诉浏览器我这个数据是每个源都可以获取就可以了。这就是 CORS 跨域资源共享。
这样的话,任何源都可以通过 AJAX 发起请求来获取我们提供的数据。针对不同语言的服务器后端有不一样的处理方法,但是实质是一样的。
七:NGINX 代理跨域
· nginx 配置解决 iconfont 跨域
浏览器跨域访问 js、css、img 等常规静态资源被同源策略许可,但 iconfont 字体文件(eot|otf|ttf|woff|svg)例外,此时可在 nginx 的静态资源服务器中加入以下配置。
· nginx 反向代理接口跨域
跨域原理:同源策略是浏览器的安全策略,不是 HTTP 协议的一部分。服务器端调用 HTTP 接口只是使用 HTTP 协议,不会执行 JS 脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。
nginx 具体配置:
八:nodejs 跨域
其实这种办法和上一种用 nginx 的方法是差不多的。都是你把请求发给一个中间人,由于中间人没有同源策略,他可以直接代理或者通过爬虫或者其他的手段得到想到的数据,然后返回(是不是和 VPN 的原理有点类似)。
当然现在常见的就是用 nodejs 作为数据的中间件,同样,不同的语言有不同的方法,但是本质是一样的。我上次自己同构自己的博客页面,用 react 服务器端渲染,因为浏览器的同源策略,请求不到数据,然后就用了 nodejs 作为中间件来代理请求数据。
九:webSocket 跨域
webSocket 大家应该都有所耳闻,主要是为了客服端和服务端进行全双工的通信。但是这种通信是可以进行跨端口的。所以说我们可以用这个漏洞来进行跨域数据的交互。
所以说,我们可以很轻松的构建基于 webSocket 构建一个客服端和服务端。代码在 github 建议大家都多多去运行一下,了解清楚。
评论