Overview
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin. Because the Datafree Reach solution manipulates the domain names, it is quite common to fail these rules with strange consequences.
There are a number of dimensions to CORS. This document only covers some common issues when supporting a datafree site. It simplifies situations, if a more complex understanding is necessary, see here.
Please note that CORS is only one of the security methods that need to be considered when developing a Reach Application - refer to this article to see a comparison with Content Security which will also need to be reviewed https://devonblog.com/security/difference-between-cors-and-csp-security-headers/
Server Side Access Control
In this implementation the backend server is programmed with a defined list of domains that are valid to use a resource, access an API etc. If the request comes from a page (domain) that is not in that list the server responds with a failure (the exact nature of the failed response can vary across websites).
With CORS a request is sent with extra headers e.g:
Access-Control-Request-Headers : content-typeAccess-Control-Request-Method : POSTOrigin : https://za.govchat.org
Note: Often a special request between the browser and the backend may occur to pre-check the security rules. This is generated automatically by the browser and it normally uses HTTP request method: OPTIONS.
A good response will contain a number of special headers e.g:
Access-control-allow-credentials : trueAccess-control-allow-headers : DNT,X-CustomHeader,...,authorizationAccess-control-allow-methods : GET, POST, DELETE, HEAD, PUTAccess-control-allow-origin : https://za.govchat.org
On some websites the server will simply respond with the domain name that appears in the Origin request header placed in the Access-control-allow-origin response header. If this happens, Reach should just work OK. On some websites the backend server will validate that the domain name is one it knows about and if it is unknown then it will not send back the required headers. It may just send back:
Access-control-allow-credentials : true
On other sites it may send back the domain that it allows but that it is not the domain the browser knows is the origin of the request so it will be equivalent to a failed response.
With the Reach product the domain name sent in the request header is the ‘free’ domain eg:
Origin : https://govchat-prod.datafree.co.za
The backend server is validating domains against a defined list it will not recognise the domain and will fail to send back the required header.
Example server code:
- <?php>
- // We'll be granting access to only the za.govchat.org domain
- // which we think is safe to access this resource as application/xml
- if($_SERVER['HTTP_ORIGIN'] == "http://za.govchat.org") {
- header('Access-Control-Allow-Origin: http://za.govchat.org');
- header('Content-type: application/xml');
- readfile('aNetResource.xml');
- } else {
- ….
For the browser when it receives a failed response and it will not send further requests to that server this may be hard to detect in logs, browser network activity etc.
Datafree Reach Solution
The backend server needs to be updated to allow the ‘free’ domain.
Warning: The Sandbox and Production environments for Reach use different domain names. It is important to get both added to the list of allowed domains.
More Details: Server Side Access Control
Server Allows Fixed Domain
Most servers will send back the response with the header ‘Access-control-allow-origin’ populated with the domain sent in the request header ‘Origin’ (for Reach this will usually contain a ’free’ domain. This is not required and it may send a fixed domain name to every request.
If the domain sent in the ‘Access-control-allow-origin’ is not the domain name of the requesting page (i.e. the Reach ‘free’ domain) the browser will treat this as equivalent to a failed response.
Domain Mapping not Defined
With CORS the browser may make an initial request to the backend to verify that the security rules are allowed (called pre-flighting - typically using HTTP method=OPTIONS) before it makes the main request. If the domain name is not defined as a legitimate domain mapping in the Datafree Gateway configuration the request will fail. It may be quite invisible to the end user.
To identify this problem use browser tools to inspect the network traffic. Look for requests with HTTP method = OPTIONS. These may be showing a HTTP response code = 200 but are actually failing in the backend as the required Access-Control headers are not being sent. Subsequent requests to the domain name are suppressed by the browser due to the failed security check so the problem has low visibility.
Note: Requests that fail due to CORS are not always obvious. For security reasons the browsers usually only display these errors in the browser tool’s Console.
Testing
When testing it is hard to identify what is being sent to the browser when it is blocked.
Also, when modifying Security Headers with javascript rules (coming soon) it is hard to debug the code. There are 2 extensions for Chrome that can be used to disable security checking and allow you to see the actual headers returned from the website:
Disable CORS: https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf/related?hl=en
Disable Content Security Policy: https://chrome.google.com/webstore/detail/disable-content-security/ieelmcmcagommplceebfedjlakkhpden?hl=en
Here is an example of the Chrome Console Log for a normally blocked CORS request:
Access to XMLHttpRequest at 'https://googleads-g-doubleclick-net-reachtest-dev.systest.datafree.co.za/pagead/id' from origin 'https://www-youtube-com-reachtest-dev.systest.datafree.co.za' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
And if the CORS is turned off this is the Console Entry for the same url:
WPvGqX-TXP0:1 Access to XMLHttpRequest at 'https://googleads-g-doubleclick-net-reachtest-dev.systest.datafree.co.za/pagead/id' from origin 'https://www-youtube-com-reachtest-dev.systest.datafree.co.za' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.