CORS

From W3C Wiki

The text below was previously part of the CORS specification. It was taken out per a suggestion at the May 2012 F2F so it could be maintained by the wider web community.

See also CORS Enabled.

Requirements

  1. Must not introduce attack vectors to servers that are only protected only by a firewall.
  2. The solution should not introduce additional attack vectors against services that are protected only by way of firewalls. This requirement addresses "intranet" style services authorize any requests that can be sent to the service.

Note that this requirement does not preclude HEAD, OPTIONS, or GET requests (even with ambient authentication and session information).

  1. It should not be possible to perform cross-origin non-safe operations, i.e., HTTP operations except for GET, HEAD, and OPTIONS, without an authorization check being performed.
  2. Should try to prevent dictionary-based, distributed, brute-force attacks that try to get login accounts to 3rd party servers, to the extent possible.
  3. Should properly enforce security policy in the face of commonly deployed proxy servers sitting between the user agent and any of servers with whom the user agent is communicating.
  4. Should not allow loading and exposing of resources from 3rd party servers without explicit consent of these servers as such resources can contain sensitive information.
  5. Must not require content authors or site maintainers to implement new or additional security protections to preserve their existing level of security protection.
  6. Must be deployable to IIS and Apache without requiring actions by the server administrator in a configuration where the user can upload static files, run serverside scripts (such as PHP, ASP, and CGI), control headers, and control authorization, but only do this for URLs under a given set of subdirectories on the server.
  7. Must be able to deploy support for cross-origin GET requests without having to use server-side scripting (such as PHP, ASP, or CGI) on IIS and Apache.
  8. The solution must be applicable to arbitrary media types. It must be deployable without requiring special packaging of resources, or changes to resources' content.
  9. It should be possible to configure distinct cross-origin authorization policies for different target resources that reside within the same origin.
  10. It should be possible to distribute content of any type. Likewise, it should be possible to transmit content of any type to the server if the API in use allows such functionality.
  11. It should be possible to allow only specific servers, or sets of servers to fetch the resource.
  12. Must not require that the server filters the entity body of the resource in order to deny cross-origin access to all resources on the server.
  13. Cross-origin requests should not require API changes other than allowing cross-origin requests. This means that the following examples should work for resources residing on http://test.example (modulo changes to the respective specifications to allow cross-origin requests):
client.open("GET", "http://example.org/data.text")
client.send()
  1. It should be possible to issue methods other than GET to the server, such as POST and DELETE.
  2. Should be compatible with commonly used HTTP authentication and session management mechanisms. I.e. on an IIS server where authentication and session management is generally done by the server before ASP pages execute this should be doable also for requests coming from cross-origin requests. Same thing applies to PHP on Apache.
  3. Should reduce the risk of inadvertently allowing access when it is not intended. This is, it should be clear to the content provider when access is granted and when it is not.

Use Cases

The main motivation behind Cross-Origin Resource Sharing (CORS) was to remove the same origin restriction from various APIs so that resources can be shared among different origins (i.e. servers).

XMLHttpRequest (XHR)

Currently if you have an API on the server at https://calendar.example/add that accepts requests using the HTTP PUT method to add new appointments you can only issue such requests from within the browser environment on resources within the https://calendar.example/ origin, as follows:

var client = new XMLHttpRequest()
client.open("PUT", "https://calendar.example/add")
client.onload = requestSuccess
client.onerror = requestError
client.onabort = requestError
client.send(appointment)

If the https://calendar.example/add resource implements CORS it can accept requests from other origins. To do this the server has to indicate it is willing to handle HTTP PUT methods for non same-origin requests in response to a preflight request. Further when the actual request is issued it has to indicate it is willing to share any response data. Code Web application developers use to talk with this resource can however remain unmodified, even when put on another origin.

If there is an API on http://foo.example.org/ that allows authenticated users to edit resources, CORS could be used to allow users to use http://editor.example/ as editor without the need of proxies when communicating changes to resources (e.g. addition or removal).

Not tainting the canvas element (HTML)

Currently if you have an image editor implemented using the canvas element at http://unicornimages.example and a clip art collection at http://narwhalart.example drawing the clip art on the canvas element will cause it to be tainted because the images are from a different origin. The effect of a tainted canvas element is that the toDataURL() method call in the following snippet will throw:

var canvas, context, clipart = []
function init() {
  canvas = document.getElementsByTagName("canvas")[0]
  context = canvas.getContext("2d")
}
function preload() {
  // populates clipart with five images from
  // http://narwhalart.example/archives/[0-9]
  // all represented as HTML <img> elements
  …
}
function draw(clipart) {
  context.drawImage(clipart, …)
}
function save() {
  // get data out of <canvas> and process it
  var data = canvas.toDataURL()
  …
}

Using CORS the maintainer of http://narwhalart.example can very easily indicate that all images can be used by http://unicornimages.example (or in fact all origins). To do so all that is required to change is that the server has to add the following HTTP headers for the clip art resources:

access-control-allow-origin: http://unicornimages.example
access-control-allow-credentials: true

This would also make the toDataURL() method call no longer throw.

Getting metadata out of media elements (HTML)

At some point in the future the HTML video and audio elements will give a programmatic API to access their metadata. This could be as simple as the following snippet shows:

The API itself is pure speculation and its specifics are not relevant for explaining how CORS can be used.

var vid = document.querySelector("video"),
    vidAuthor = vid.meta.author

To prevent data theft this API will only work if the media resource is same origin with where the script is executed from. However, if the video were annotated with CORS, similarly to the image resource in the previous use case, this could work just fine.

Server-Sent Events (EVENTSOURCE)

Currently if http://example.org/news exposes a stream of news events only resources on http://example.org can make use of it. With CORS it would be very easy to allow http://international.example.org to access the stream of news as well. If this news stream is personalized e.g. by the means of cookies it only requires one additional response header for http://international.example.org to be able to make use of it:
access-control-allow-origin: http://international.example.org
access-control-allow-credentials: true

The code used by Web authors would remain near identical (identical if they use an absolute URL):

stream = EventSource("http://example.org/news")
stream.onmessage = function(e) { … }

xml-stylesheet processing instruction (XMLSS)

Currently cross-origin loads of XSLT resources are prohibited to prevent data theft (e.g. from an intranet). With CORS an XSLT resource http://static.example.org/generic can easily be used by http://example.org resources by adding an additional HTTP header to the resource. Again, the code used by Web authors remains the same:
<?xml-stylesheet href="http://static.example.org/generic"?>

Design Decision FAQ

Why is there a preflight request?

For most type of requests two resource sharing checks are performed. Initially a "permission to make the request" check is done on the response to the preflight request. And then a "permission to read" check is done on the response to the actual request. Both of these checks need to succeed in order for success to be relayed to the API (e.g. XMLHttpRequest). The "permission to make the request" check is performed because deployed servers do not expect such cross-origin requests. E.g., a request using the HTTP DELETE method. If they reply positively to the preflight request the client knows it can go ahead and perform the actual desired request.

Why is POST treated similarly to GET?

Cross-origin POST requests have long been possible using the HTML form element. However, this is only the case when Content-Type is set to one of the media types allowed by HTML forms.

Why can cookies and authentication be included in the request?

Sending cookies and authentication information enables user-specific cross-origin APIs. Cookies and authentication information is already sent cross-origin for various HTML elements, such as img, script, and form.

Why can cookies and authentication information not be provided by the script author for the request?

This would allow dictionary based, distributed, cookies / user credentials search.

Why is the client the policy enforcement point?

The client already is the policy enforcement point for these requests. The mechanism allows the server to opt-in to let the client expose the data. Something clients currently not do and which servers rely upon. Note however that the server is in full control. Based on the value of the Origin header in cross-origin requests it can decide to return no data at all or not provide the necessary handshake (the Access-Control-Allow-Origin header).

What about the JSONRequest proposal?

JSONRequest has been considered by the Web Applications Working Group and the group has concluded that it does not meet the documented [#requirements requirements]. JSONRequest is a specific API and cannot handle e.g. cross-origin XSLT through <?xml-stylesheet?> or the same scenarios same-origin XMLHttpRequest can handle today in cross-origin fashion, e.g. manipulating resources making use of the REST architectural style.