初会同源策略和CORS


17-56028048_p0.png

0x01 何为同源策略

如果两个页面同协议、同域名(host)、同端口,那么就可以认为这两个页面同源。

# URL 是否同源 备注
0 http://image.baidu.com base
1 http://image.baidu.com/search/index.html Y
2 https://image.baidu.com N 协议不同
3 http://image.baidu.com:8080 N 端口不同
4 http://eclick.baidu.com N host不同

在页面中用 about:blankjavascript: URL执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URLs 没有明确包含有关原始服务器的信息。

这种策略的作用:限制了不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。如果恶意脚本请求了一个非同源的东西,那么这种行为会因为同源策略而被浏览器限制


0x02 跨域访问

网站为了追求访问速度通常将一些静态文件(cssjs图片)放在 CDN 上,那么 CDN 与网站肯定不同域,可为什么又能请求成功呢?

在使用XMLHttpRequest<img> 标签时又会受到同源策略的约束而失败,那到底在什么情况下会触发同源策略呢?

页面跨域总的来说就分为三类:

  • 允许跨域写操作(Cross-origin writes)
  • 允许跨域资源嵌入(Cross-origin embedding)
  • 不允许跨域读操作(Cross-origin reads)

允许跨域写操作 就的例如链接(links),重定向以及表单提交;允许跨域资源嵌入 那么到底允许那些资源可以嵌入呢?一般就是以下几类:

  • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
  • <link rel="stylesheet" href="..."> 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type 消息头
  • <img>嵌入图片
  • <video><audio>嵌入多媒体资源
  • <object>, <embed><applet> 的插件
  • @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts
  • <frame><iframe> 载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互

0x03 CORS 跨域资源共享

当一个域的资源请求另一个域的资源的时候,资源会发起一个跨域HTTP请求

比如,一个<img>通过src请求获取其他服务器上的一个图片资源时,出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequestFetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源

跨域并不一定是浏览器限制了跨站请求,也有可能是请求发送成功,浏览器拦截了响应;有些浏览器会拦截 HTTPS 跨域访问 HTTP ,ChromeFirefox便会拦截



跨域资源共享CORS机制允许 Web 应用服务器进行跨域访问控制.CORS 允许在下列场景中使用跨域 HTTP 请求:

  • 前文提到的由 XMLHttpRequestFetch 发起的跨域 HTTP 请 求
  • Web 字体,通过 @font-face 进行跨域调用
  • WebGL 贴图
  • 使用 drawImage 将 Images/video 画面绘制到 canvas
  • 样式表(使用 CSSOM)
  • scripts

0x04 CORS 的预请求

跨域资源共享标准规定对于某些可能对服务器产生副作用的请求(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预请求(preflight request)询问服务器是否同意跨域,允许后才真正发起http请求。

并不是所有的请求都会触发 preflight request,满足以下任何一项条件才可以触发:

  • 使用除了下面请求外的其他请求,下面这些请求不会触发 CORS 预检请求,我们称这样的请求为简单请求

    • GET
    • HEAD
    • POST
  • Content-Type不是下列之一:

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • HTTP 首部字段不能包含下列以外的值:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • 请求中使用了ReadableStream对象

  • 请求中的XMLHttpRequestUpload对象注册了任意多个事件监听器

凡是满足上述条件的请求,将被触发preflight request。在preflight request的返回头中,会包含一些关于是否允许发起跨域的信息。下面的例子是一个 POST XML内容的预请求:

OPTIONS /resources/post-here/ 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
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

// 响应

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


在这个OPTIONS请求中,有两个特殊的首部字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

首部字段Access-Control-Request-Method 告知服务器,这个OPTIONS请求的实际请求会使用 POST方法,首部字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHERContent-Type。服务器据此决定,该实际请求是否被允许。

在预检请求的响应中,要注意一下四个字段:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-TEST, Content-Type
Access-Control-Max-Age: 86400

上面的字段表明服务器将接受后续的实际请求。

第一行表明 允许来自http://foo.example的请求
第二行表明 服务器允许客户端使用 POST, GETOPTIONS 方法发起请求
第三行表明 服务器允许请求中携带字段 X-PINGOTHERContent-Type
最后一行表明 该响应的有效时间为 86400 秒,也就是 24 小时,在此期间,无须为同一请求再次发起预检请求



CORS 还有一个有趣的特性是,可以基于 cookiesHTTP 认证信息发送身份凭证。一般而言,对于跨域 XMLHttpRequestFetch 请求,浏览器不会发送身份凭证信息,如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。

如果发起的是一个简单请求,那么不会经过preflight,但是,如果服务端返回的信息中没有Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。


Ex:

baidu.com跨域请求http://127.0.0.1/php/01-corsHeader.php

  • 01-corsHeader.php内容:
<?php

header("Access-Control-Allow-Origin: https://www.baidu.com"); 

echo "ok";



XMLHttpRequest

headers


  • 修改01-corsHeader.php内容:
<?php

header("Access-Control-Allow-Origin: http://127.0.0.1"); 

echo "ok";



XMLHttpRequest

headers


0.End 参考文章

Same-origin policy
Cross-Origin Resource Sharing (CORS)
HTTP访问控制(CORS)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容