关于CORS的理解

跨域

Posted by EWL on August 4, 2018

CORS(cross-origin-resource-sharing)

从我的理解上讲,就是在违反同源规则的情况下,从另一个域名下拿到资源信息。 在CORS的角度讲,分为两种请求:

  1. 简单请求
  2. 非简单请求
简单请求

条件一: 请求方法是以下三种方法之一: —- HEAD

—- GET

—- POST

条件二: HTTP的头信息不超出以下几种字段: —- Accept

—- Accept-Language

—- Content-Language

—- Last-Event-ID

—- Content-Type:只限于三个值application/x-www-form-urlencoded、 multipart/form-data、text/plain

同时满足以上两种条件,就是简单请求。

而在跨域发出简单请求的时候,浏览器会在请求头部加上一个字段origin,里面包含着请求所在页面的信息: 协议+ 域名+端口 当请求发送到服务端的时候,如果请求的origin处于服务端的可允许访问名单中,那么就可以返回请求的资源,并且服务器返回的响应,会多出几个头信息字段。 (1)Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

关于以上几个字段的Tips: Cache-Control:Cache-Control 指令控制谁在什么条件下可以缓存响应以及可以缓存多久。 Content-Language: 用来说明访问者希望采用的语言或语言组合,这样的话用户就可以根据自己偏好的语言来定制不同的内容。 Content-Type:用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些Asp网页点击的结果却是下载到的一个文件或一张图片的原因。 Expires:表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同max-age的效果。但是如果同时存在,则被Cache-Control的max-age覆盖。 Last-Modified:有些数据随时都在变化。 CNN.com 的主页经常几分钟就更新。另一方面,Google.com 的主页几个星期才更新一次 (当他们上传特殊的假日 logo,或为一个新服务作广告时)。 Web 服务是不变的:通常服务器知道你所请求的数据的最后修改时间,并且 HTTP 为服务器提供了一种将最近修改数据连同你请求的数据一同发送的方法。 如果你第二次 (或第三次,或第四次) 请求相同的数据,你可以告诉服务器你上一次获得的最后修改日期:在你的请求中发送一个 If-Modified-Since 头信息,它包含了上一次从服务器连同数据所获得的日期。如果数据从那时起没有改变,服务器将返回一个特殊的 HTTP 状态代码 304,这意味着 “从上一次请求后这个数据没有改变”。这一点有何进步呢?当服务器发送状态编码 304 时,不再重新发送数据。您仅仅获得了这个状态代码。所以当数据没有更新时,你不需要一次又一次地下载相同的数据;服务器假定你有本地的缓存数据。 Pragma: 是一个在 HTTP/1.0 中规定的通用首部,这个首部的效果依赖于不同的实现,所以在“请求-响应”链中可能会有不同的效果。它用来向后兼容只支持 HTTP/1.0 协议的缓存服务器,那时候 HTTP/1.1 协议中的 Cache-Control 还没有出来

expires的使用格式 格式: Expires = “Expires” “:” HTTP-date 例如 Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式)

否则,就会返回一个正常的http响应,这个响应的头信息没有包括access-control-allow-origin,此时浏览器就会抛出一个错误,被xmlhttprequest的onerror捕获。

注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200

非简单请求

而所谓的非简单请求,对简单请求的定义取反即可。 非简单请求发生时,会发生一次预检请求,之后才会发生真正的请求,且与前面的简单请求类似。

预检请求

image.png

image.png

image.png

image.png

以上图片中展示出的前两张表示预检请求,可以看到请求方式为options,且请求头包括两个特殊字段。

(1)Access-Control-Request-Method

该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法(put delete等等)。

(2)Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。

结果:一旦服务器通过了”预检”请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

参考: 阮一峰老师关于CORS的讲解

MDN关于HTTP头部的内容

关于CORS的配置安全漏洞报告及最佳部署实践