跨域资源共享

W3C规范地址: http://www.w3.org/TR/cors/
摘要
本文档定义了一套允许客户端发送跨域请求的机制。其他类似允许API跨域访问资源的规范可以使用本文档的算法。比如API在http://example.org域下,要访问的资源在http://hello-world.example域下,在http://hello-world.example下的资源就可以本规范定义的机制(比如,加一个Access-Control-Allow-Origin到response里)来允许http://example.org跨域访问该资源。
1 简介
客户代理(浏览器--译者注)大多同源机制来限制网络请求。这一机制阻止运行在一个域下客户端程序读取另一个域下的数据,同时也限制程序自动发送非安全HTTP请求到别的域。
符合这一模式的客户代理,通常发出去的跨域请求都带着用户的机密信息,比如HTTP authentication或者cookie等。
本规范在以下几方面扩展了这一模式:
--Response中可以加一个叫Access-Control-Allow-Origin的header,这个header的值写上允许访问本资源的域,来实现跨域访问。客户代理负责验证这个header的值和发出请求的域是否一致。
--客户代理可以通过叫preflight request的请求来判断某个资源是否支持跨域的,非简单HTTP方法的请求。同样,客户代理负责验证。
--通过检查Origin header,服务端可以检查HTTP请求是否是跨域的。通过这一扩展,服务端就可以更加灵活的控制如何处理跨域的请求了(比如什么也不返回)。
本规范是CROS API规范的基础,CROS API规范规定了如何使用本规范,比如在Server-Sent Events和XMLHttpRequest里的使用。

CROS wiki page为本规范提供了更多补充:
假如在http://example.com/hello有一个简单的文本资源,该资源就是一个字符串“Hello World!”,再假设想让http://hello-world.example访问这一资源,这时候按照本规范,response应该是这样的:
Access-Control-Allow-Origin: http://hello-world.example

Hello World!
http://hello-world.example端,可以用XMLHttpRequest访问该资源:
var client = new XMLHttpRequest()
client.open("GET", "http://example.com/hello")
client.onreadystatechange = function(){/do something/}
client.send()
当处理非简单HTTP方法的跨域请求时候,资源所有者还需要点额外的工作。首先要能相应一个叫做preflight的请求,该请求用OPTIONS方法发送,然后要处理真正的请求(比如一个DELETW请求)。 对于preflight请求的response应该包括如下header:
Access-Control-Allow-Origin: http://hello-world.example
Access-Control-Max-Age: 3628800
Access-Control-Allow-Methods: PUT,DELETE
Access-Control-Max-Age表示response缓存的时间,在该时间内,后续的请求都不用再发preflight请求了。Access-Control-Allow-Mehtods规定了真正的请求可以使用的HTTP方法。对于真正请求的response可以只包括这个header:
Access-Control-Allow-Origin: http//hello-world.example
多发的那个preflight请求是用户代理的工作。比如,资源在http://calendar.example/app下,我们可以通过如下ECMAScript(Javascript的标准--译者注)代码来访问:
function deleteItem(itemId, updateUI){
var cleint = new XMLHttpRequest();
client.open("DELETE", "http://calendar.example/app")
client.onload = updateUI
client.onerro = updateUI
cleint.onabort = updateUI
client.send("id=" + tiemId)
}

2 一致性
本规范目标读者是资源拥有者和客户代理。同时,也介绍了一些基于本规范的规范,即CORS API Specifications, 在安全性一章还包括一些client端程序的安全建议。
非规范部分包括附录及标注为非规范部分的章节,此外所有的图标例子和提示都是非规范的。除了以上部分,剩下所有内容都是规范部分。
对实现了本规范所有需求的资源可称为符合标准的资源。
对实现了本规范所有需求的用户代理可称为符合标准的用户代理。
只要最终结果跟本规范描述的一致,用户代理和资源拥有者可以使用任何算法来实现本规范。

3 术语
本规范部分术语来自于以下规范: The Web Origin Concept, HTML, HTTP and URI。
本规范使用的大多数术语都是通用的,但是也有部分术语只限于本规范使用。
以大小写敏感(case-sensitive)的方式比较两个字符串表示一个码点一个码点的比较。
以ASCII非大小写命感的( case-insensitive)方式来比较两个字符串表示一个码点一个码点的比较,除了以下情况,在U+0041 大写字母 A 到U+005A大写字母Z范围内和在U+0061小写字母a到U+0071小写字母z,这两组中对应的大小写字母被认为是匹配的。
将字符串转换成小写字母表示把在U+0041大写字母A至U+005A答谢字母Z范围内替换成U+0061小写字母a至U+007A小写字母z的相应字母。
术语用户认证在本规范中表示cookeis,HTTP authentication还有客户端SSL证书这些用户代理在当前域下使用过的信息。注意,不包括代理的认证信息或者Origin header.
术语跨域(corss-origin)表示不同域。
简单HTTP方法表示method以大小写不敏感的方式匹配以下名字:
GET
HEAD
POST
如果header以ASCII大小写不敏感的方式跟以下名字匹配,那么这个header就是简单header: Accept, Accept-Language, Content-Language, 还有一种简单header情况就是header名字ASCII大小写不敏感匹配Content-Type并且此header的值ASCII大小写不敏感匹配application/x-www-form-urlencoded, multipart/form-data,或者text/plain.
如果response header名字ASCII大小写不敏感匹配下列内容,则称之为简单response header:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
解析header的时候一定要按照ABNF标准。如果header不符合该标准,则报错说header解析失败。

4 安全
以下属于非规范部分。
本规范通篇都涉及安全类的需求和策略。以下提供的安全建议只适用于本规范。
简单跨越请求是指跟用户代理能发送请求完全等价的请求,这种请求不在本规范考虑范围内。对于这些不在本规范内的简单跨域请求(比如通过跨域的表单(form)提交的GET或者POST请求,或者使用了HTML script元素发送的GET请求(JSONP就是用了这个方式--译者注)通常都会带着用户认证信息,因此符合本规范的资源一定要考虑到如何处理这些带着用户认证信息的简单跨域请求。
正因为如此,除了那些简单查询的资源,其他资源都要做好抗Cross-Site Request Forgery(CSRF)攻击,可以通过在request里引入一个复杂的token来解决这一问题。
本规范规定如何允许用户代理中的跨域的应用程序访问网络资源,并且能通过HTTP Response能得到资源。这种资源不应该限定授权那些域访问,要么都允许,要么都不允许。
那些对其他域没用的资源一定不要返回Access-Control-Allow-Origin header,比如登录页面。同样要保证这种资源不受CSRF攻击,比如强制验证request里某个复杂的token。支持本规范的客户代理也对这种资源没有任何影响。
如果资源的公共开放的,不需要任何校验,那么可以直接返回Access-Control-Allow-Origin header,值设置为""。
如果GET方法的资源,并且返回的内容是ECMAScript(Javascript标准--译者注),那么可以直接返回Access-Control-Allow-Origin header,值设为"
",因为这种资源通过HTML的script标签可以直接跨域访问到,所以也没什么方法保护。如果有必要,可以用上面说的方法防止CSRF攻击。

当请求带着用户认证信息或者Origin header的时候,则需要下面一些考虑。
当请求的资源不只是简单的查询,并且依赖Origin header来区分不同域的时候,资源本身需要能区分以下两种情况:授权全部请求(包括它引起资源的变化),还是授权它获取到资源的内容。
对于授权全部请求的情况应该满足的条件是:用户授权了当前的域。
常用的办法是让用户显示的去授权某个域的跨域请求。这样,在跨域请求中带着一个用户授权的安全token,授权就清晰明了了。OAuth就是这一模式的典型代表。
以下情况可以在跨域请求中使用用户认证信息:

A cross-origin request with credentials as defined in this specification is used to substitute for alternate methods of authenticated resource sharing, such as server-to-server back channels, JSONP, or cross-document messaging. [JSONP] [HTML]

This substitution can expose additional attack surface in some cases, as a cross-site scripting vulnerability in the requesting origin can allow elevation of privileges against the requested resource when compared to a server-to-server back channel.

As a substitute for JSONP-style cross-origin credentialed requests, use of this specification significantly improves the security posture of the requesting application, as it provides cross-origin data access whereas JSONP operates via cross-origin code-injection.

As a substitute for cross-origin communication techniques relying on loading a resource, with credentials, into an HTML iframe element, and subsequently employing cross-document messaging or other cross-origin side channels, this specification provides a roughly equivalent security posture. Again, data received from origins that are not completely trusted has to be validated to conform to expected formats and authorized values.

For requests to resources that have no significance other than retrieval, and where the credentials are used only to provide user-specific customization for otherwise publicly accessible information. In this case, restricting access to certain origins may protect user privacy by preventing customizations from being used to identify a user, except at authorized origins.

When this specification is used for requests which have significance other than retrieval and which involve coordination between or data originating from more than two origins, (e.g. between resources enabling editing, printing and storage, each at distinct origins) requests ought to set the omit credentials flag and resources ought to perform authorization using security tokens explicitly provided in the content of the request, especially if the origins are not all mutually and completely trusted.

In such multi-origin scenarios, a malicious resource at one of the origins may be able to enlist the user-agent as a confused deputy and elevate its privileges by abusing user credentials sent with cross-origin requests. Avoiding such attacks requires that the coordinating applications have explicit knowledge of the scope of privilege for each origin and that all parameters and instructions received are carefully validated at each step in the coordination to ensure that effects implied do not exceed the authority of the originating principal. [CONFUSED]

Given the difficulty of avoiding such vulnerabilities in multi-origin interactions it is recommended that, instead of using user credentials automatically attached to the request by the user agent, security tokens which specify the particular capabilities and resources authorized be passed as part of the explicit content of a request. OAuth again provides an example of such a pattern.

Authors of client-side Web applications are strongly encouraged to validate content retrieved from a cross-origin resource as it might be harmful.

Web applications that are not uniquely identified by specific host names, and/or mapped to specific ports, do not necessarily have a unique origin, and thus will not be able to securely utilize the mechanism defined in this specification. This is because an origin is composed of only the scheme, hostname, and port.

For example, a web application whose URL is of the type example.org/app-name/ and the app-name portion is necessary to distinguish the web application from other web applications also running at example.org, will be unable to securely employ the mechanism defined in this specification.

Mapping web applications to distinct origins is vital for secure web applications.

2014-10-24 22:22238