同源安全策略
默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为。但是,实现合理的跨域请求对开发某些浏览器应用程序也是至关重要的。
一、CORS
Cross-Origin Resource Sharing,跨域资源共享
1、原理
CORS是 W3C 的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
比如一个简单的使用 GET 或 POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain。在发送该请求时,需要给它附加一个额外的Origin 头部,其中包含请求页面的源信息(协议、域名和端口) ,以便服务器根据这个头部信息来决定是否给予响应。下面是 Origin 头部的一个示例:
Origin: http://www.nczonline.net
如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 “*” )。例如:
Access-Control-Allow-Origin:http://www.nczonline.net
如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求和响应都不包含 cookie 信息。
2、IE对CORS的实现
微软在 IE8 中引入了 XDR( XDomainRequest )类型。这个对象与 XHR 类似,但能实现安全可靠的跨域通信。XDR 对象的安全机制部分实现了 W3C 的 CORS 规范。以下是 XDR 与 XHR 的一些不同之处。
cookie 不会随请求发送,也不会随响应返回。
只能设置请求头部信息中的 Content-Type 字段。
不能访问响应头部信息。
只支持 GET 和 POST 请求。
这些变化使 CSRF(Cross-Site Request Forgery,跨站点请求伪造)和 XSS(Cross-Site Scripting,跨站点脚本)的问题得到了缓解。被请求的资源可以根据它认为合适的任意数据(用户代理、来源页面等)来决定是否设置 Access-Control- Allow-Origin 头部。作为请求的一部分, Origin 头部的值表示请求的来源域,以便远程资源明确地识别 XDR 请求。
XDR对象的使用方法与 XHR对象非常相似。 也是创建一个 XDomainRequest 的实例, 调用 open()方法,再调用 send() 方法。但与 XHR 对象的 open() 方法不同,XDR 对象的 open() 方法只接收两个参数:请求的类型和 URL。
所有 XDR 请求都是异步执行的,不能用它来创建同步请求。
3、其他浏览器对CORS的实现
通过 XMLHttpRequest对象实现了对 CORS 的原生支持。在尝试打开不同来源的资源时,无需额外编写代码就可以触发这个行为。 要请求位于另一个域中的资源, 使用标准的 XHR对象并在 open() 方法中传入绝对 URL即可。
xhr.open("get", "http://www.somewhere-else.com/page/", true);
与 IE 中的 XDR 对象不同,通过跨域 XHR 对象可以访问 status 和 statusText 属性,而且还支持同步请求。跨域 XHR 对象也有一些限制,但为了安全这些限制是必需的。以下就是这些限制。
不能使用 setRequestHeader() 设置自定义头部。
不能发送和接收 cookie。
调用 getAllResponseHeaders() 方法总会返回空字符串。
由于无论同源请求还是跨源请求都使用相同的接口,因此对于本地资源,最好使用相对 URL,在访问远程资源时再使用绝对 URL。这样做能消除歧义,避免出现限制访问头部或本地 cookie 信息等问题。
4、Preflighted Reqeusts
CORS 通过一种叫做 Preflighted Requests 的透明服务器验证机制支持开发人员使用自定义的头部、GET 或 POST之外的方法,以及不同类型的主体内容。在使用下列高级选项来发送请求时,就会向服务器发送一个 Preflight 请求。这种请求使用 OPTIONS 方法,发送下列头部。
Origin :与简单的请求相同。
Access-Control-Request-Method :请求自身使用的方法。
Access-Control-Request-Headers :(可选)自定义的头部信息,多个头部以逗号分隔。
以下是一个带有自定义头部 NCZ 的使用 POST 方法发送的请求。
Origin: http://www.nczonline.net
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ
发送这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通。
Access-Control-Allow-Origin :与简单的请求相同。
Access-Control-Allow-Methods :允许的方法,多个方法以逗号分隔。
Access-Control-Allow-Headers :允许的头部,多个头部以逗号分隔。
Access-Control-Max-Age :应该将这个 Preflight 请求缓存多长时间(以秒表示)。
例如:
Access-Control-Allow-Origin: http://www.nczonline.net
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000
Preflight 请求结束后,结果将按照响应中指定的时间缓存起来。而为此付出的代价只是第一次发送这种请求时会多一次 HTTP 请求。
5、带凭据的请求
默认情况下,跨源请求不提供凭据(cookie、HTTP 认证及客户端 SSL 证明等) 。通过将withCredentials 属性设置为 true ,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请求,会用下面的 HTTP 头部来响应。
Access-Control-Allow-Credentials: true</