Example 1 – CORS request with JavaScript
The following example explains the CORS syntax, without actually doing anything with the responseText
request:
- Create the
XMLHttpRequest
object: - The
createCORSRequest
function does the following:- Defines the new
XMLHttpRequest
request as the variable "xhr".
- Checks whether the browser supports CORS via
XHR
by detecting the withCredentials
or XDomainRequest
properties. - Opens the request for a resource on the target domain.
- These are the parameters passed to
createCORSRequest(method, url)
:- The method would typically be
GET
, POST
, or another method - The URL is the URI of the resource requested by the local domain
IE 10 supports the working draft XMLHttpRequest
level 2. Therefore, the withCredentials
property can be used to detect CORS support in most browsers, including IE >= 10. To provide backwards compatibility for IE < 10, use its XDomainRequest
property.
Tip
Microsoft Internet Explorer 9 makes up 9.14% of desktop browsers, so we must include a fallback check for its the XDomainRequest
property when withCredentials
fails.
Passing a request to a utility function
This request does two things:
- If
createCORSRequest()
returns an XHR
object, it sends the request - When the XHR ready state is loaded--request.onload--do something with
request.responseText
Example 2: the CORS transaction to retrieve the title tag
This CORS request retrieves the page title from the target domain, targetdomain.com
. It parses the responseText
request to get the title and sends the target domain and the retrieved page title text to the console.log
:
What happens in this CORS request?
- The
XMLHttpRequest
request is created with the detection of CORS and error handling. - The
responseText
request returns the contents of the page at targetdomain.com
with GET
. - The
getTitle
function is executed on the responseText
request, and it returns the title text. - The target domain URL and the title text are sent to the
console.log
.
You're probably thinking, "Big deal! I can get the title text in other ways.". But you could do more than retrieving a DOM element.
Distributing DOM elements to multiple domains
Let's consider a scenario in which you want to distribute a block-level DOM element, for example, a navigation menu from a target domain to multiple pages on multiple domains, along with customized CSS and JavaScript for the menu. You only change the navigation menu once on the target domain and copy it to multiple pages on multiple domains with CORS.
We will examine the pieces and then put them all together.
A script
tag on the local domain embeds a script from the target domain. The same origin policy allows script
tags to request resources across domains. The CORS script on the target domain will contain the createCORSRequest
function and a request like this:
The CORS request allows you to GET
a PHP file from the target domain and use it on the local domain.
You are not limited to requesting the HTML for a page on the target domain in the responseText
request, as in example 2; header.php
on the target domain may contain HTML, CSS, JavaScript, and any other code that is allowed in a PHP file.
Note
We are only reading header.php
. Its contents are created by a process on the target domain outside of CORS. A script on the target domain scrapes the navigation menu and adds the necessary CSS and JavaScript. This script may be run as a Cron job to automatically update header.php
, and it can also be triggered manually by an administrator on the target domain.
If the request is successful, it returns the contents of header.php and replaces the contents of a DOM element #global-header
on the local domain with the responseText
request:
Adding the Access-Control-Allow-Origin
header in header.php
on the target domain allows access from the local domain. Since header.php
is a PHP file, we add the header with the PHP code. Use the wildcard *
to allow access from any domain because we making the CORS request from multiple domains:
You can place the CORS request script on the target domain as cors_script.js
and trigger it with the script tag on your local domain. The responseText
request is sent to any local domain page that contains the script tag. The DOM selector #global-header is replaced on the local domain with the responseText
request contents of header.php
, which contains the navigation menu HTML, CSS, and JavaScript from the target domain. We are also going to replace the logo image on the local domain with the one from the target domain.
By placing a script tag on your local domain page, you can access a target domain from any local domain, run a script on it, and do something on your local domain:
The contents of cors_script.js
in the target domain are as follows:
The navigation menu is automatically distributed to any number of pages on any number of domains via CORS whenever a page with the script tag loads.
Securing when all domains are whitelisted
What happens if someone copies your script tag to some other domain where the script was not intended to run? Since we whitelisted access from any domain with Access-Control-Allow-Origin: *
, the request will be allowed from any domain; if the page also has the matching DOM selector #global-header
, the script will copy the content from the target domain to the page making the request.
Although the W3C specification for CORS recommends providing a list of allowed origins, in practice, this is not widely implemented in browsers.
Tip
Ways to add security when a CORS header whitelists all domains
Techniques have been proposed to first match $_SERVER['HTTP_ORIGIN']
to an allowed list, then write the header that allows the matched origin. Since $_SERVER['HTTP_ORIGIN']
is not reliable, or the requesting domain may be served via a CDN that does not match the expected domain, this technique may not work.
An alternative method is to add allowed domains in .htaccess
or in the server conf, which may have the same trouble with CDN domains.
Methods to add security when a CORS header whitelists all domains
There are a few methods to secure when all the domains are whitelisted in the CORS header. The following code compares the HTTP_ORIGIN
with a list of allowed domains; if it matches, then the CORS header is written using the matched domain:
This technique may not work because $_SERVER['HTTP_ORIGIN']
is not reliable, or the requesting domain may be served via a CDN that does not match the expected domain.
An alternative method is to add allowed domains in .htaccess
or in the server conf, which may have the same trouble with CDN domains.
Note
CORS headers can not provide reliable security
The CORS headers give browser information about allowed domains, but some other security policies, such as cookies or OAuth, can enforce tighter security in your application.