今天故事的主角,还是大家熟悉的二狗子。二狗子拿到了项目奖金,奖励了自己之后,决定把剩下的钱定期存入银行。他用浏览器访问www.bank.com,输入用户名和密码,成功登录。bank.com返回一个cookie来识别用户二狗子。不得不说,浏览器是一个认真负责的工具。它会记录这个cookie。每次二狗子向bank.com发出HTTP请求时,浏览器都会准确地将cookie添加到HTTP请求头中。一起发给bank.com,让bank.com知道二狗子登录了,就可以按照二狗子的要求做一些事情,比如查询余额、转账、取款等。二狗子存完钱后,看着自己账户上的余额,暗暗高兴。于是,他打开美女网看他最喜欢的电影。但是二狗子不知道的是,浏览器将meinv.com的HTML和JavaScript下载到了本地,并开始执行。在其中一个JavaScript中,秘密创建了一个XMLHttpRequest对象,然后向bank.com发出了一个HTTP请求。浏览器严格遵守规定,将之前存储的cookie添加到HTTP请求中。但是bank.com并不知道这个HTTP请求是meinv.com的JavaScript发出的,还以为是二狗子发出的。bank.com查看cookie发现是登录用户,于是认真执行请求命令,二狗子个人信息泄露。(ps.实际上,实现这样的攻击不会那么简单,银行网站肯定采取了很多其他的安全验证措施,这个故事只是用来说明基本原理。)可怜的二狗子还不知道发生了什么,蒙受金钱损失。那我们就帮他回顾一下为什么会这样。首先,无论何时访问bank.com,无论是点击按钮访问链接还是通过程序,存储在浏览器中的bank.comcookie都会被传递。其次,从meinv.com下载的JavaScript使用XMLHttp访问bank.com。我们无法阻止第一点。如果被阻止,cookie将失去其主要功能。第二点,浏览器必须限制meinv.com的JavaScript访问bank.com。这个限制就是同源策略。同源策略浏览器提供了fetchAPI或者XMLHttpRequest等方法,可以让我们方便快捷的向后端发起请求,获取资源,并在前端展示。通过fetchAPI或XMLHttpRequest发起的HTTP请求必须遵守同源策略。那么什么是同源策略呢?同源策略(same-originpolicy)规定当浏览器使用JavaScript发起HTTP请求时,如果请求的域名是同源的,则请求不受限制。但是,如果是非同源请求,则会强制遵守CORS(Cross-OriginResourceSharing,跨源资源共享)规范,否则浏览器会拦截该请求。那么什么是同源呢?同源策略非常严格,要求两个url必须同时满足以下三个条件才算同源:1.协议(http/https)相同;2、域名(domain)相同;3、端口(端口)相同。例如:以下哪些URL地址与https://www.bank.com/withdraw...同源?https://www.bank.com/save.html(?)http://www.bank.com/withdraw....(?,协议不一样)https://bank.com/login.html(?,域名不一样)https://www.bank.com:8080/wit...(?,端口不一样)所以,当我们请求不同来源的URL地址时,一个跨域的HTTP请求(跨域http请求)。比如我们想在https://www.upyun.com的页面上显示来自https://opentalk.upyun.com的信息,我们使用浏览器提供的fetchAPI发起请求:try{fetch('https://opentalk.upyun.com/data')}catch(err){console.error(err);}生成跨域请求,必须符合CORS规范。当请求的服务器没有配置允许CORS访问或者源地址不允许时,请求会失败,在Chrome的开发者工具控制台会看到如下经典错误:Accesstofetchat'https://opentalk.upyun来自“https://www.upyun.com”的.com/data'已被CORS策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以在禁用CORS的情况下获取资源。那么我们在实际应用中如何正确设置CORS呢?什么是CORSCORS是对来自不同来源(域)的请求的规范。当浏览器请求不同域的资源时,跨域请求的服务器必须明确告知浏览器它允许什么样的请求。只有在服务器允许范围内的请求才能被浏览器放行和请求,否则会被浏览器拦截,访问失败。在CORS规范中,跨域请求主要分为两种:简单请求和非简单请求。简单请求一个简单请求必须满足以下四个条件。在实际开发中,我们一般只关注前两种情况:(1)使用GET、POST、HEAD方法中的一种;(2)只使用以下安全请求头,不要人为设置其他请求头:AcceptAccept-LanguageContent-LanguageContent-Type仅限于以下三种类型:text/plainmultipart/form-dataapplication/x-www-form-urlencoded(3)请求中的任何XMLHttpRequestUpload对象都没有注册任何事件Listener,可以使用XMLHttpRequest.upload属性访问XMLHttpRequestUpload对象;(4)请求中没有使用ReadableStream对象。不满足以上任一条件的请求为非简单请求。浏览器以不同方式处理简单请求和非简单请求。对于简单的请求,浏览器直接发起CORS请求。具体的,在请求头信息中,自动添加Origin(来源)字段。Origin的值包括请求协议、域名和端口三部分,用于表示本次请求来自哪个源。服务器可以根据这个值决定是否同意这个请求。例如下面的请求头消息:GET/dataHTTP/2Host:opentalk.upyun.comaccept-encoding:deflate,gzipacaccept:*/*origin:https://www.upyun.com......如果Origin指定如果源不在服务器允许的范围内,服务器将返回一个正常的HTTP响应。如果浏览器发现响应头中不包含Access-Control-Allow-Origin字段,则会抛出错误。需要注意的是,这种错误无法通过状态码来识别,HTTP响应的状态码可能是200。如果Origin指定的源在允许的范围内,响应头中会包含以下字段:Access-Control-Allow-Origin:https://www.upyun.comAccess-Control-Allow-Headers:AuthorizationAccess-Control-Expose-Headers:X-DateAccess-Control-Allow-Credentials:true你可能还会看到一个功能,与CORS请求相关的字段以Access-Control-开头。如果允许跨源请求,则响应标头必须包含Access-Control-Allow-Origin标头。它的值要么是请求中Origin字段的值,要么是一个*,表示接受任何域名的请求。Access-Control-Allow-Credentials为可选字段,其值为布尔值,表示是否允许发送cookie。如果在发起跨域请求时将withCredentials标志设置为true,则浏览器在发起跨域请求时也会同时向服务器发送一个cookie。如果服务器的响应中不存在Access-Control-Allow-Credentials标头,则浏览器不会响应内容。特别是,如果请求端设置了withCredentials,Access-Control-Allow-Origin的值必须是具体的域名值,不能设置为*,否则浏览器会抛出跨域错误。Access-Control-Expose-Headers也是一个可选的标头。跨域请求时,XMLHttpRequest对象的getResponseHeader()方法只能获取6个基本响应字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma如果开发者需要获取其他响应头字段,或者一些自定义响应标头,服务器可以通过设置Access-Control-Expose-Headers标头来指定发起者可访问的响应标头。非简单请求非简单请求往往是对服务器有特殊要求的请求,比如请求方法为PUT或DELETE,或者Content-Type字段类型为application/json。对于不是简单请求的CORS请求,浏览器会在正式发起跨域请求之前添加一个HTTP查询请求,我们称之为预检请求(preflight)。浏览器首先会询问服务器当前域名是否在服务器的权限列表中,可以使用哪些HTTP请求方式和请求头字段。只有收到肯定的回答,浏览器才会发出正式的跨域请求,否则会报错。比如我们使用代码发起一个跨域请求:fetch('http://opentalk.upyun.com/data/',{method:'PUT',headers:{'Content-Type':'application/json','X-CUSTOM-HEADER':'123'}})浏览器会发现这是一个非简单的请求,它会自动发送一个OPTIONS预检请求,核心内容有两部分,Access-Control-Request-Method表示后续的跨域请求使用的方法,Access-Control-Request-Headers表示后续的跨域请求头中会有这样的内容。OPTIONS/data/HTTP/1.1Host:opentalk.upyun.comOrigin:http://www.upyun.comAccess-Control-Request-Method:PUTAccess-Control-Request-Headers:X-MY-CUSTOM-HEADER,Content-Type服务端收到preflight请求后,会检查自己是否可以接受这些特殊的请求方法和headers。如果被接受,以下信息将包含在响应头中:HTTP/1.1200OKAccess-Control-Allow-Origin:*Access-Control-Allow-Methods:GET,HEAD,POST,PUT,DELETE,OPTIONS,PATCHAccess-Control-Max-Age:86400Access-Control-Allow-Headers:X-Date,range,X-Custom-Header,Content-TypeAccess-Control-Expose-Headers:X-Date,X-File,Content-type...在上面的HTTP响应,关键是Access-Control-Allow-Origin字段,*表示任何跨域请求都可以请求数据。我们在简单请求中已经对一些字段进行了说明,这里有几个需要注意说明的header。Access-Control-Allow-Methods,这个是不可缺少的字段,它的值是一个逗号分隔的字符串,表示服务器支持的所有跨域请求方式。Access-Control-Allow-Headers字段是一个逗号分隔的字符串,表示服务器支持的所有请求头字段,不限于浏览器在预检中请求的字段。Access-Control-Max-Age:该字段可选,用于指定本次预检请求的有效期,单位为秒。上述结果中,有效期为1天(86400秒),在此期间,无需再次发送preflightrequest。以上就是对CORS的简单介绍。如果你使用优派云的CDN或者云存储服务,在访问的时候遇到跨域的问题,你可以非常快捷方便的配置CORS。登录服务控制台,进入:服务管理>功能配置>访问控制>CORS跨域共享,点击【管理】按钮开始配置。如下图所示:相信看完本文后,您对配置界面的各个字段都不再陌生了。推荐阅读网络安全(一):常见网络威胁及防范【白话科普】从《熊猫烧香》谈计算机病毒
