同源策略限制了一个源(origin)中加载文本或脚本与来自其它源(origin)中资源的交互方式。javascript
若是两个页面拥有相同的协议(protocol),端口(若是指定),和主机, 那么这两个页面就属于同一个源(origin)。
下表给出了相对http://store.company.com/dir/page.html同源检测的示例:html
页面能够改变自己的源,但会受到一些限制。脚本能够设置document.domain 的值为当前域的一个后缀,若是这样作的话,短的域将做为后续同源检测的依据。java
例如,假设在 http://store.company.com/dir/other.html中的一个脚本执行了下列语句:web
document.domain = "company.com";
这条语句执行以后,页面将会成功地经过对 http://company.com/dir/page.html 的同源检测。ajax
浏览器单独保存端口号。任何的赋值操做,包括document.domain = document.domain都会以null值覆盖掉原来的端口号。 所以company.com:8080页面的脚本不能仅经过设置document.domain = "company.com"就能与company.com通讯。赋值时必须带上端口号,以确保端口号不会为null。shell
附注:使用document.domain来让子域安全地访问其父域,须要同时将子域和父域的document.domain设置为相同的值。必需要这么作,即便是简单的将父域设置为其原来的值。没有这么作的话可能致使受权错误。后端
可是实践中有一些场景须要跨域的读写,因此出现了一些hack的方式来跨域。好比在同域内作一个代理,JSON-P等。 但这些方式都存在缺陷,没法完美的实现跨域读写。因此在XMLHttpRequest v2标准下,提出了CORS(Cross Origin Resourse-Sharing)的模型,试图提供安全方便的跨域读写资源。目前主流浏览器均支持CORS。跨域
CORS定义了两种跨域请求,简单跨域请求和非简单跨域请求。浏览器
当一个跨域请求发送简单跨域请求包括缓存
提及来比较别扭,简单的意思就是设置了一个白名单,符合这个条件的才是简单请求。其余不符合的都是非简单请求。以下图所示:
之因此有这个分类是由于浏览器对简单请求和非简单请求的处理机制是不同的。当咱们须要发送一个跨域请求的时候,浏览器会首先检查这个请求,若是它符合上面所述的简单跨域请求,浏览器就会马上发送这个请求。
若是浏览器检查以后发现这是一个非简单请求,好比请求头含有X-Forwarded-For字段。这时候浏览器不会立刻发送这个请求,而是有一个preflight,跟服务器验证的过程。浏览器先发送一个options方法的预检请求。下图是一个示例。若是预检经过,则发送这个请求,不然就不拒绝发送这个跨域请求。
下面详细分析一下实现安全跨域请求的控制方式。先看一下非简单请求的预检过程。
Origin: 普通的HTTP请求也会带有,在CORS中专门做为Origin信息供后端比对,代表来源域。 Access-Control-Request-Method: 接下来请求的方法,例如PUT, DELETE等等 Access-Control-Request-Headers: 自定义的头部,全部用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中
Access-Control-Allow-Origin: Access-Control-Allow-Methods: Access-Control-Allow-Headers:
Access-Control-Allow-Origin: 容许跨域访问的域,能够是一个域的列表,也能够是通配符"*"。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://foo.example/subdir/ 是无效的。可是不一样子域名须要分开设置,这里的规则能够参照同源策略 Access-Control-Allow-Credentials: 是否容许请求带有验证信息,这部分将会在下面详细解释 Access-Control-Expose-Headers: 容许脚本访问的返回头,请求成功后,脚本能够在XMLHttpRequest中访问这些头的信息(貌似webkit没有实现这个) Access-Control-Max-Age: 缓存这次请求的秒数。在这个时间范围内,全部同类型的请求都将再也不发送预检请求而是直接使用这次返回的头做为判断依据,很是有用,大幅优化请求次数 Access-Control-Allow-Methods: 容许使用的请求方法,以逗号隔开 Access-Control-Allow-Headers: 容许自定义的头部,以逗号隔开,大小写不敏感
而后浏览器经过返回结果的这些控制字段来决定是将结果开放给客户端脚本读取仍是屏蔽掉。若是服务器没有配置cors,返回结果没有控制字段,浏览器会屏蔽脚本对返回信息的读取。
你们注意这个流程。服务器接收到跨域请求的时候,并无先验证,而是先处理了请求。因此从某种程度上来讲。在支持cors的浏览器上实现跨域的写资源,打破了传统同源策略下不能跨域读写资源。
再一个就是若是程序猿偷懒将Access-Control-Allow-Origin设置为容许来自全部域的跨域请求。那么cors的安全机制几乎就无效了。不过先别高兴的太早。其实这里在设计的时候有一个很好的限制。xmlhttprequest发送的请求须要使用“withCredentials”来带上cookie,若是一个目标域设置成了容许任意域的跨域请求,这个请求又带着cookie的话,这个请求是不合法的。(就是若是须要实现带cookie的跨域请求,须要明确的配置容许来源的域,使用任意域的配置是不合法的)浏览器会屏蔽掉返回的结果。javascript就无法获取返回的数据了。这是cors模型最后一道防线。假如没有这个限制的话,那么javascript就能够获取返回数据中的csrf token,以及各类敏感数据。这个限制极大的下降了cors的风险。
从思路上讲,有两种类型的攻击方式。
Access-Control-Allow-Origin: * 容许任何来自任意域的跨域请求
用户访问恶意网页的时候,执行了到内网服务器192.168.1.123/password.txt的请求,脚本在接收到服务器返回以后,将内容发送到攻击者的服务器上。
目前来讲,解决思路有两种
Access-Control-Allow-Origin: allow.domain.com