看书看到一句话:跨域限制一般只在浏览器端存在,对于服务端或iOS,Android等客户端是不存在的。
感觉非常的疑惑怪,所以仔细查了一些资料,分析一下这个问题。
浏览器的同源策略限制
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
同源的概念:如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。
另外,同源策略又分为以下两种:
- DOM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
- XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。
如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:
- 做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com。
- 把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
- 这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。
如果 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:
- 用户登录了自己的银行页面 http://mybank.com,http://mybank.com 向用户的 cookie 中添加用户标识。
- 用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。
- http://evil.com 向 http://mybank.com 发起 AJAX HTTP 请求,请求会默认把 http://mybank.com 对应 cookie 也同时发送过去。
- 银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。
- 而且由于 Ajax 在后台执行,用户无法感知这一过程。
所以同源策略是一个浏览器的安全机制。
因此在不做特殊设置的情况下,我们是没有办法在自己写的页面里,跨域嵌套不同源的DOM,或者访问不同域的接口的,这就是所谓跨域访问的问题。
如何跨域访问接口
JSONP
这方面的介绍非常多,大概意思就是js是可以跨域下载的,所以只要把要请求的东西,封装在一个js文件中,就可以很容易的读取和解析了。
-
jsonp的核心则是动态添加<script>标签来调用服务器 提供的js脚本。
-
缺点就是只支持GET请求。
CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),具体可以参考 跨域资源共享 CORS 详解,写的非常清楚完整。
总的来说就是需要服务器支持。
那我们在开发时候需要调用一些别人的资源怎么办呢?常用的是下个方法。
代理
如果我们请求的时候还是用前端的域名,然后有个东西帮我们把这个请求转发一下,转化成符合要求的后端域名,就解决了彻底解决跨域的问题了。
举个知乎的例子。
因为是前端开发,所以我用Node.js的request库做代理。
npm install request --save-dev
然后新建一个proxy.js
const http = require('http');
const request = require('request');
const hostname = '127.0.0.1';
const port = 8010;
const imgPort = 8011;
// 创建一个 API 代理服务
const apiServer = http.createServer((req, res) => {
const url = 'http://news-at.zhihu.com/api/4' + req.url;
const options = {
url: url
};
function callback (error, response, body) {
if (!error && response.statusCode === 200) {
// 设置编码类型,否则中文会显示为乱码
res.setHeader('Content-Type', 'text/plain;charset=UTF-8');
// 设置所有域允许跨域
res.setHeader('Access-Control-Allow-Origin', '*');
// 返回代理后的内容
res.end(body);
}
}
request.get(options, callback);
});
// 监听 8010 端口
apiServer.listen(port, hostname, () => {
console.log(`接口代理运行在 http://${hostname}:${port}/`);
});
// 创建一个图片代理服务
const imgServer = http.createServer((req, res) => {
const url = req.url.split('/img/')[1];
const options = {
url: url,
encoding: null
};
function callback (error, response, body) {
if (!error && response.statusCode === 200) {
const contentType = response.headers['content-type'];
res.setHeader('Content-Type', contentType);
res.setHeader('Access-Control-Allow-Origin', '*');
res.end(body);
}
}
request.get(options, callback);
});
// 监听 8011 端口
imgServer.listen(imgPort, hostname, () => {
console.log(`图片代理运行在 http://${hostname}:${imgPort}/`);
});
然后启动这个server就好了
$ node proxy.js
客户端有没有跨域问题?
所以文章开头说的话是对的吗?从某种角度确实是对的,因为客户端本身就不存在同不同源的概念,我们可以写代码请求任意源的服务器,只要满足条件,就能得到想要的response。所以这样看,不存在跨域问题。
但是在客户端开发角度上,还是需要考虑这个问题的,因为有WebView存在,就一定会有跨域访问的问题。
Android WebView跨域漏洞
2017年底,腾讯爆了一个支付宝被克隆漏洞,仔细看原理的话,就是利用了Android WebView跨域的漏洞
- WebView中setAllowFileAccessFromFileURLs 或setAllowUniversalAccessFromFileURLsAPI配置为true
- WebView可以直接被外部调用,并能够加载外部可控的HTML文件
满足这两个条件时,攻击者可以通过URL Scheme的方式,可远程打开并加载恶意HTML文件,远程获取APP中包括用户登录凭证在内的所有本地敏感数据。
网友评论