乐趣区

你不知道的CORS跨域资源共享

了解下同源策略

源(origin)*:就是协议、域名和端口号;

同源:就是源相同,即协议、域名和端口完全相同;

同源策略:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源;

同源策略的分类:

DOM 同源策略:即针对于 DOM,禁止对不同源页面的 DOM 进行操作; 如不同域名的 iframe 是限制互相访问。

XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。

不受同源策略限制:

页面中的链接,重定向以及表单提交 (因为表单提交,数据提交到 action 域后,本身页面就和其没有关系了,不会管请求结果,后面操作都交给了 action 里面的域) 是不会受到同源策略限制的。
资源的引入不受限制,但是 js 不能读写加载的内容:如嵌入到页面中的 <script src=”…”></script>,<img>,<link>,<iframe> 等

为什么要跨域限制

如果没有 DOM 同源策略:那么就没有啥 xss 的研究了,因为你的网站将不是你的网站,而是大家的,谁都可以写个代码操作你的网站界面

如果没有 XMLHttpRequest 同源策略,那么就可以很轻易的进行 CSRF(跨站请求伪造)攻击:

用户登录了自己的网站页面 a.com,cookie 中添加了用户标识。
用户浏览了恶意页面 b.com,执行了页面中的恶意 AJAX 请求代码。

b.com 向 a.com 发起 AJAX HTTP 请求,请求会默认把 a.com 对应 cookie 也同时发送过去。

a.com 从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据; 数据就泄露了。而且由于 Ajax 在后台执行,这一过程用户是无法感知的。

(附)有了 XMLHttpRequest 同源策略就可以限制 CSRF 攻击?别忘了还有不受同源策略的:表单提交和资源引入,(安全问题下期在研究)

跨域决解方案

JSONP 跨域:借鉴于 script 标签不受浏览器同源策略的影响,允许跨域引用资源;因此可以通过动态创建 script 标签,然后利用 src 属性进行跨域;
缺点:

所有网站都可以拿到数据,存在安全性问题,需要网站双方商议基础 token 的身份验证。
只能是 GET,不能 POST。
可能被注入恶意代码,篡改页面内容,可以采用字符串过滤来规避此问题。

服务器代理:浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端。
document.domain、window.name、location.hash:借助于 iframe 决解 DOM 同源策略
postMessage:决解 DOM 同源策略,新方案
CORS(跨域资源共享):这里讲的重点

CORS(跨域资源共享)
HTML5 提供的标准跨域解决方案,是一个由浏览器共同遵循的一套控制策略,通过 HTTP 的 Header 来进行交互;主要通过后端来设置 CORS 配置项。
CORS 简单使用

之前说得 CORS 跨域,嗯嗯,后端设置 Access-Control-Allow-Origin:*|[或具体的域名]就好了;
第一次尝试:

app.use(async(ctx,next) => {
ctx.set({
“Access-Control-Allow-Origin”: “http://localhost:8088”
})
发现有些请求可以成功,但是有些还是会报错:

请求被同源策略阻止,预请求的响应没有通过检查:http 返回的不是 ok?
并且发现发送的是 OPTIONS 请求:

发现:CORS 规范将请求分为两种类型,一种是简单请求,另外一种是带预检的非简单请求

简单请求和非简单请求

浏览器发送跨域请求判断方式:

浏览器在发送跨域请求的时候,会先判断下是简单请求还是非简单请求,如果是简单请求,就先执行服务端程序,然后浏览器才会判断是否跨域;
而对于非简单请求,浏览器会在发送实际请求之前先发送一个 OPTIONS 的 HTTP 请求来判断服务器是否能接受该跨域请求;如果不能接受的话,浏览器会直接阻止接下来实际请求的发生。

什么是简单请求

请求方法是如下之一:

GET
HEAD
POST

所有的 Header 都只包含如下列表中(没有自定义 header):

Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma

除此之外都是非简单请求
CORS 非简单请求配置须知

正如上图报错显示,对于非简单请求,浏览器会先发送 options 预检,预检通过后才会发送真是的请求;
发送 options 预检请求将关于接下来的真实请求的信息给服务器:

Origin:请求的源域信息
Access-Control-Request-Method:接下来的请求类型,如 POST、GET 等
Access-Control-Request-Headers:接下来的请求中包含的用户显式设置的 Header 列表
服务器端收到请求之后,会根据附带的信息来判断是否允许该跨域请求,通过 Header 返回信息:
Access-Control-Allow-Origin:允许跨域的 Origin 列表
Access-Control-Allow-Methods:允许跨域的方法列表
Access-Control-Allow-Headers:允许跨域的 Header 列表, 防止遗漏 Header,因此建议没有特殊需求的情况下设置为 *
Access-Control-Expose-Headers:允许暴露给 JavaScript 代码的 Header 列表
Access-Control-Max-Age:最大的浏览器预检请求缓存时间,单位为 s

CORS 完整配置
koa 配置 CORS 跨域资源共享中间件:
const cors = (origin) => {
return async (ctx, next) => {
ctx.set({
“Access-Control-Allow-Origin”: origin, // 允许的源
})
// 预检请求
if (ctx.request.method == “OPTIONS”) {
ctx.set({
‘Access-Control-Allow-Methods’: ‘OPTIONS,HEAD,DELETE,GET,PUT,POST’, // 支持跨域的方法
‘Access-Control-Allow-Headers’: ‘*’, // 允许的头
‘Access-Control-Max-Age’:10000, // 预检请求缓存时间
// 如果服务器设置 Access-Control-Allow-Credentials 为 true,那么就不能再设置 Access-Control-Allow-Origin 为 *, 必须用具体的域名
‘Access-Control-Allow-Credentials’:true // 跨域请求携带身份信息(Credential,例如 Cookie 或者 HTTP 认证信息)
});
ctx.send(null, ‘ 预检请求 ’)
} else {
// 真实请求
await next()
}
}
}

export default cors
现在不管是简单请求还是非简单请求都可以跨域访问啦~
跨域时如何处理 cookie

cookie:

我们知道 http 时无状态的,所以在维持用户状态时,我们一般会使用 cookie;
cookie 每次同源请求都会携带;但是跨域时 cookie 是不会进行携带发送的;

问题:
由于 cookie 对于不同源是不能进行操作的;这就导致,服务器无法进行 cookie 设置,浏览器也没法携带给服务器(场景:用户登录进行登录操作后,发现响应中有 set-cookie 但是,浏览器 cookie 并没有相应的 cookie)

决解:

浏览器请求设置 withCredentials 为 true 即可让该跨域请求携带 Cookie;使用 axios 配置 axios.defaults.withCredentials = true

服务器设置 Access-Control-Allow-Credentials=true 允许跨域请求携带 Cookie

“积跬步、行千里”—— 持续更新中~,喜欢的话留下个赞和关注哦!

往期经典好文:

服务器(CentOS)安装配置 mongodb
Koa 日志中间件封装开发(log4js)
团队合作必备的 Git 操作
使用 pm2 部署 node 生产环境

退出移动版