
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:blank 或 javascript: URL执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URLs 没有明确包含有关原始服务器的信息。
这种策略的作用:限制了不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。如果恶意脚本请求了一个非同源的东西,那么这种行为会因为同源策略而被浏览器限制
0x02 跨域访问
网站为了追求访问速度通常将一些静态文件(css、js、图片)放在 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请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源
跨域并不一定是浏览器限制了跨站请求,也有可能是请求发送成功,浏览器拦截了响应;有些浏览器会拦截 HTTPS 跨域访问 HTTP ,
Chrome和Firefox便会拦截
跨域资源共享CORS机制允许 Web 应用服务器进行跨域访问控制.CORS 允许在下列场景中使用跨域 HTTP 请求:
- 前文提到的由
XMLHttpRequest或Fetch发起的跨域 HTTP 请 求 - Web 字体,通过
@font-face进行跨域调用 - WebGL 贴图
- 使用 drawImage 将 Images/video 画面绘制到 canvas
- 样式表(使用 CSSOM)
- scripts
0x04 CORS 的预请求
跨域资源共享标准规定对于某些可能对服务器产生副作用的请求(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用
OPTIONS方法发起一个预请求(preflight request)询问服务器是否同意跨域,允许后才真正发起http请求。
并不是所有的请求都会触发 preflight request,满足以下任何一项条件才可以触发:
-
使用除了下面请求外的其他请求,下面这些请求不会触发 CORS 预检请求,我们称这样的请求为
简单请求:GETHEADPOST
-
Content-Type值不是下列之一:application/x-www-form-urlencodedmultipart/form-datatext/plain
-
HTTP 首部字段不能包含下列以外的值:
AcceptAccept-LanguageContent-LanguageContent-TypeDPRDownlinkSave-DataViewport-WidthWidth
请求中使用了
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-PINGOTHER 与 Content-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,GET和OPTIONS方法发起请求
第三行表明 服务器允许请求中携带字段X-PINGOTHER与Content-Type
最后一行表明 该响应的有效时间为86400秒,也就是 24 小时,在此期间,无须为同一请求再次发起预检请求
CORS 还有一个有趣的特性是,可以基于 cookies和 HTTP 认证信息发送身份凭证。一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息,如果要发送凭证信息,需要设置 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";

- 修改
01-corsHeader.php内容:
<?php
header("Access-Control-Allow-Origin: http://127.0.0.1");
echo "ok";

0.End 参考文章
Same-origin policy
Cross-Origin Resource Sharing (CORS)
HTTP访问控制(CORS)