安全

功能概述

  • 出于安全原因,浏览器限制从脚本内发起的跨域HTTP请求 或 拦截了跨域请求的结果。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
    • 避免用户访问恶意网站时,恶意网站向攻击站点发起请求并携带保存在缓存中的用户信息。即同源策略限制了1、不同源禁止访问缓存 2、不同源禁止发起请求。但存在着例外的情况,需要绕开这些限制所以出现了CORS。通过服务器的响应头来为信任的域名开放访问当前服务器资源的权限
  • 跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
  • 规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 OPTIONS 方法发起一个预检请求,从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求
    • 对服务器数据产生副作用的 HTTP 请求方法(是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求)
    • 在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
  • CORS请求失败会产生错误,但是为了安全,在JavaScript代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

什么情况下需要 CORS

  • 由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
  • Web 字体 (CSS 中通过 @font-face 使用跨域字体资源)
  • WebGL 贴图
  • 使用 drawImage 将 Images/video 画面绘制到 canvas
  • 样式表(使用 CSSOM)(通过JS操作CSS树时使用到的资源)
    • CSS Object Model(CSSOM)是所有CSS选择器的映射,以及每个选择器的相关属性(以树的形式),具有根节点、兄弟、后代、子级和其他关系。(样式树)
    • CSS对象模型也是一组允许从JavaScript操作CSS的API。

若干访问控制场景(如何设置服务器响应头实现对CORS的支持)

简单请求

  • 某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”。若请求满足所有下述条件,则该请求可视为“简单请求”:(注意是所有)
    • 使用下列方法之一
      • GET
      • HEAD:请求资源的头部信息, 并且这些头部与 HTTP GET 方法请求时返回的一致.
      • POST:发送数据给服务器
    • Fetch 规范定义了对 CORS 安全的首部字段集合,以下是可以人为设定的首部字段:
      • Accept:用来告知服务器,客户端可以处理的内容类型,这种内容类型用MIME类型来表示。
      • Content-Type 仅限于下列三者之一 :Content-Type 实体头部用于指示资源的MIME类型 media type
        • text/plain 文本文件默认值。即使它意味着未知的文本文件,但浏览器认为是可以直接展示的。
        • multipart/form-data 用于HTML表单从浏览器发送信息给服务器。
        • application/x-www-form-urlencoded 数据被编码为名称/值对。标准的编码格式,在get中表现为(name1=value1&name2=value2…)
      • Accept-Language:允许客户端声明它可以理解的自然语言,以及优先选择的区域方言。
        • 借助内容协商机制,服务器可以从诸多备选项中选择一项进行应用, 并使用Content-Language 应答头通知客户端它的选择。
        • 浏览器会基于其用户界面语言来为这个请求头设置合适的值。注意:用户可能会想要浏览非本地用户界面语言的页面。
      • Content-Language:是一个 entity header (实体消息首部),用来说明访问者希望采用的语言或语言组合,这样的话用户就可以根据自己偏好的语言来定制不同的内容。(应该是用在响应中,告诉用户该实体适用于使用何种语言的人)
        • 假如设置了这样一条消息首部( "Content-Language: de-DE" ),那么说明这份文件是为说德语的人提供的(当然这并不意味着文件本身就是用德语写的)
        • 如果没有指明 Content-Language,那么默认地,文件内容是提供给所有语言的访问者使用的。
      • DPR(草案阶段的请求头)
      • Downlink(草案阶段的请求头)
      • Save-Data(草案阶段的请求头)
      • Viewport-Width(草案阶段的请求头)
      • Width(草案阶段的请求头)
    • 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器(没有对XMLHttpRequestUpload 对象注册监听)
      • XMLHttpRequest.upload 属性返回一个 XMLHttpRequestUpload对象,用来表示上传的进度
    • 请求中没有使用 ReadableStream 对象。
      • 流操作API中的ReadableStream 接口呈现了一个可读取的二进制流操作。
      • Fetch API 通过Response的属性 body提供了一个具体的 ReadableStream 对象。https://www.cnblogs.com/qq3279338858/p/11057795.html
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[XML Data]
  • Origin 表明该请求来源于。该首部用于 CORS 请求或者 POST 请求。除了不包含路径信息,该字段与 Referer 首部字段相似。
    • 有时候将该字段的值置空是有用的,例如,资源由一个 data URL 指定。
    • 不管是否为跨域请求,ORIGIN 字段总是被发送。
  • Referer 首部包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。
    • 服务端一般使用 Referer 首部识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等。
    • 在以下两种情况下,Referer 不会被发送:
      • 来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI;
      • 当前请求页面采用的是非安全协议,而来源页面采用的是安全协议(HTTPS)。
  • Vary 是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。
    • 它被服务器用来表明在 content negotiation algorithm(内容协商算法)中选择一个资源代表的时候应该使用哪些头部信息(headers).
    • 例如你提供给移动端的内容是不同的,可用防止你客户端误使用了用于桌面端的缓存。Vary: User-Agent
  • Access-Control-Allow-Origin 响应头指定了该响应的资源是否被允许与给定的origin共享。
    • 对于不需具备凭证(credentials)的请求,服务器会以“*”作为通配符,从而允许所有域都具有访问资源的权限。
    • 如需允许https://developer.mozilla.org访问您的资源,您可以设置:Access-Control-Allow-Origin: https://developer.mozilla.org
    • 如果服务器未使用“*”,而是指定了一个域,那么为了向客户端表明服务器的返回会根据Origin请求头而有所不同,必须在Vary响应头中包含Origin。(要使用该缓存,新请求的Origin必须和缓存请求的相同)

预检请求

  • 当请求满足下述任一条件时,即应首先发送预检请求:
    • 使用了下面任一 HTTP 方法:
      • PUT:使用请求中的负载创建或者替换目标资源
      • DELETE
      • CONNECT:开启一个客户端与所请求资源之间的双向沟通的通道。
      • OPTIONS:用于获取目的资源所支持的通信选项。
      • TRACE:实现沿通向目标资源的路径的消息环回(loop-back)测试 ,提供了一种实用的 debug 机制。(测试接口状态的方法?)
      • PATCH:用于对资源进行部分修改,不同于 PUT 方法,而与 POST 方法类似,PATCH 方法是非幂等的,这就意味着连续多个的相同请求会产生不同的效果。
    • 人为设置了 CORS 安全的首部字段集合之外的其他首部字段。(注意是之外,例如自定义头部)
    • Content-Type 的值不属于简单请求的那几类
    • 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
    • 请求中使用了ReadableStream对象。
  • 大多数浏览器不支持针对于预检请求的重定向。如果一个预检请求发生了重定向,浏览器将报告错误(已被废弃,需要注意浏览器是否实现)
 1.OPTIONS /resources/post-here/ HTTP/1.1
 2.Host: bar.other
 3.User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
 4.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 5.Accept-Language: en-us,en;q=0.5
 6.Accept-Encoding: gzip,deflate
 7.Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 8.Connection: keep-alive
 9.Origin: http://foo.example
10.Access-Control-Request-Method: POST
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type
12.
13.
14.HTTP/1.1 200 OK
15.Date: Mon, 01 Dec 2008 01:15:39 GMT
16.Server: Apache/2.0.61 (Unix)
17.Access-Control-Allow-Origin: http://foo.example
18.Access-Control-Allow-Methods: POST, GET, OPTIONS
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
20.Access-Control-Max-Age: 86400
21.Vary: Accept-Encoding, Origin
22.Content-Encoding: gzip
23.Content-Length: 0
24.Keep-Alive: timeout=2, max=100
25.Connection: Keep-Alive
26.Content-Type: text/plain
  • Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。
  • Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。
  • Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。
  • Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。
  • Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

附带身份凭证的请求

  • 一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息(默认)
  • Fetch 与 CORS 的一个有趣的特性是,可以基于 HTTP cookies 和 HTTP 认证信息发送身份凭证?
    • Fetch API中的Request() 构造器中的credentials 选项https://www.cnblogs.com/qq3279338858/p/11057795.html
  • 将 XMLHttpRequest 的 withCredentials 标志设置为 true,从而向服务器发送 Cookieshttps://www.cnblogs.com/qq3279338858/p/10882479.html
  • 附带身份凭证的请求
    • 如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
    • 请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。
    • 如果响应首部中携带了 Set-Cookie 字段,尝试对 Cookie 进行修改。如果操作失败,将会抛出异常。
  • Access-Control-Allow-Credentials:
    • Credentials(身份凭证)可以是 cookies, authorization headers 或 TLS client certificates。
      • authorization headers:Authorization 请求消息头含有服务器用于验证用户代理身份的凭证,通常会在服务器返回401 Unauthorized 状态码以及WWW-Authenticate 消息头之后在后续请求中发送此消息头。
      • TLS client certificates TLS客户端证书,TLS是传输层安全协议,HTTPS基于它实现
    • 当作为对预检请求的响应的一部分时,这能表示是否真正的请求可以使用credentials(身份凭证)。
    • 简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。

HTTP 响应首部字段

Access-Control-Expose-Headers

  • 在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
    • Cache-Control、Expires、Last-Modified、Pragma 浏览器缓存相关头部,见https://www.cnblogs.com/qq3279338858/p/10393881.html
    • Content-Type、Content-Language 见当前文章
  • Access-Control-Expose-Headers 头让服务器把允许浏览器访问的头放入白名单Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

Access-Control-Allow-Origin

  • 前文已有

Access-Control-Max-Age

  • 前文已有

Access-Control-Allow-Credentials

  • 前文已有

Access-Control-Allow-Methods

  • 前文已有

Access-Control-Allow-Headers

  • 前文已有

HTTP 请求首部字段

  • 这些首部字段无须手动设置。 当开发者使用 XMLHttpRequest 对象发起跨域请求时,它们已经被设置就绪。

Origin

  • 前文已有

Access-Control-Request-Method

  • 前文已有

Access-Control-Request-Headers

  • 前文已有
原文地址:https://www.cnblogs.com/qq3279338858/p/11053015.html