Resolve Issues with Cross-Origin Resource Sharing

book

Article ID: 169328

calendar_today

Updated On:

Products

ProxySG Software - SGOS

Issue/Introduction

When the browser sends a cross-domain request for a resource, it adds an “Origin” header to the request. The Origin header identifies the website where the request originated. Because the header is added by the browser, its contents can’t be modified by any scripts or application code that’s running in the browser. The idea is to allow the server receiving the request to validate whether or not it trusts the page from which the request originated.
 
If the server allows the cross-domain request, then it will include an “Access-Control-Allow-Origin” header that specifies the origin server in the response. Upon receiving the response, the browser will check the contents of that header against the originating website. If they don’t match, then the browser will drop the request, and the results will not be displayed or returned to the requesting script.




 

Resolution

CORS presents a problem for authentication when using origin-cookie-redirect and form-cookie-redirect modes. If a request is received for a third-party domain that doesn’t have an auth cookie, then the SG must redirect that request to the virtual URL. As far as the browser knows, the 302/307 redirect is coming from the OCS; if the request includes an Origin header, the browser will expect an Access-Control-Allow-Origin header in the response. Such a response can be generated using CPL like this:
 
<Proxy>
    request.x_header.Origin.exists=yes action.add_allowed_domains(yes)
 
define action add_allowed_domains
    set(exception.response.x_header.Access-Control-Allow-Origin, "$(request.x_header.Origin)")
end
 

The browser will then follow the redirect to the virtual URL, but at this point we have a new problem. The original request – as initiated from HTML or JavaScript – must be “credentialed”. Only credentialed requests will contain cookies and/or cached user credentials. Unless the request is credentialed, it will be impossible for the SG to get a cookie surrogate or use credentials at the virtual URL.
 
A request is marked as “credentialed” by passing a “withCredentials” flag to XmlHttpRequest (JavaScript), or by setting a “crossorigin=use-credentials” attribute on the requesting HTML element. The SG therefore cannot force requests to be credentialed. There is no known solution for authenticating un-credentialed CORS requests.
 
Just as with the Origin domain, credentialed requests must be allowed by the server. The server signifies that by sending an “Access-Control-Allow-Credentials: true” response header. We therefore need to add that header to the CPL. Our CPL becomes:
 
<Proxy>
    request.x_header.Origin.exists=yes action.add_allowed_domains(yes)
 
define action add_allowed_domains
    set(exception.response.x_header.Access-Control-Allow-Origin, "$(request.x_header.Origin)")
    set(exception.response.x_header.Access-Control-Allow-Credentials, "true")
end
 

 
The above CPL will allow authentication to succeed, but will cause the browser to reject the OCS response. When following a redirect, the browser changes the Origin header to “null”. More info:
https://www.w3.org/TR/cors/#redirect-steps
https://wiki.mozilla.org/Security/Origin#When_Origin_is_served_.28and_when_it_is_.22null.22.29
 
The above CPL will return “null” back to the browser in the Access-Control-Allow-Origin header, and that is sufficient to pass the browser’s response validation, since “null” in the response matches the Origin header contents. However, when the OCS is loaded, its Access-Control-Allow-Origin header is forwarded to the client, and that doesn’t match the “null” response. As a result, the browser will reject the OCS response.
 
That can be addressed with the following CPL:
 
<Proxy>
    request.x_header.Origin.exists=yes action.add_allowed_domains(yes)
 
define action add_allowed_domains
    set(exception.response.x_header.Access-Control-Allow-Origin, "$(request.x_header.Origin)")
    set(exception.response.x_header.Access-Control-Allow-Credentials, "true")
    set(response.x_header.Access-Control-Allow-Origin, "$(request.x_header.Origin)")
    set(response.x_header.Access-Control-Allow-Credentials, "true")
end

 
Use the above CPL to get resolve CORS-related error messages received in the browser.