Cross-Origin Resource Sharing
External
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
- https://en.wikipedia.org/wiki/Same-origin_policy
- https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
- https://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/
- http://www.w3.org/TR/access-control/
- https://www.w3.org/TR/cors/
- RFC 6454 https://tools.ietf.org/html/rfc6454
- https://fetch.spec.whatwg.org/#http-cors-protocol
Internal
Overview
Web browsers are built so they don't allow JavaScript that runs as part of web page to make HTTP requests via the XMLHttpRequest interface to other sites than the origin web site. In some cases - Chrome, for example, the HTTP request is made, but the browser will just throw out the response and log a JavaScript error:
XMLHttpRequest cannot load http://b.com/B.html. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
The same-origin policy only applies to web fonts and AJAX (XMLHttpRequest) requests. A web page may freely embed images, stylesheets, scripts, iframes, videos and some plugin content (such as Adobe Flash) from any other domain.
A cross-origin HTTP request is one that is made to:
- A different domain.
- A different subdomain.
- A different port.
- A different protocol.
Why?
The web browser wants to prevent the situation when an user unknowingly downloads malicious behavior from a web site A, which then starts to make invocations into other site B. In order for this to be allowed, site B has to explicitly allow it - and then the browser allows it too. The browser insures this by essentially asking B: "Do you allow scripts downloaded from A to invoke into you?" This invocation is called "pre-flight".
Cross-Origin Resource Sharing
Cross-origin calls can be made with the cooperation of the second server. The standard way to do it is to employ a protocol called Cross-Origin Resource Sharing (CORS, https://fetch.spec.whatwg.org/#http-cors-protocol). The protocol consists of a simple header exchange between the client and the second server. This standard extends HTTP with a new Origin request header and a new Access-Control-Allow-Origin response header. Web browsers expect Access-Control-Allow-Headers, and Access-Control-Allow-Origin headers to be set up in each method that accepts CORS requests. In addition, as described below, some browsers first make an HTTP request to an OPTIONS method in the same resource, and then expect to receive the same headers: A browser rendering content from http://A.com wants to send a request into http://B.com so it “pre-flights” the request by inquiring the B.com server on its capabilities with an Origin header sent over an OPTIONS request:
OPTIONS / HTTP/1.1 Host: B.com Access-Control-Request-Method: GET Origin: http://A.com Referer: http://A.com/the-page-containinig-js.html
If B.com wants to allow cross-origin calls, it should respond with a pair of headers:
Access-Control-Allow-Origin: http://A.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE
To allow general access, the second server could respond with:
Access-Control-Allow-Origin: *
Origin
The origin is defined by {protol, host, port}. The algorithm to calculate the origin of a URI is specified in RFC 6454 https://tools.ietf.org/html/rfc6454/
Headers
Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
The header specifies either a single origin, which tells the browser to allow that origin to access the resource, or, for requests without credentials, the "*" wildcard, which tells the browser to allow any origin to access the resource. If the server specifies a single origin rather than the "*" wildcard, then the server should also include "Origin" in the "Vary" response header, to indicate that server responses will differ based on the value of the "Origin" request header.
Access-Control-Allow-Methods
Access-Control-Allow-Headers
The Access-Control-Allow-Headers header is used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
Access-Control-Max-Age
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
This header allows a server to whitelist headers that browsers are allowed to access:
Access-Control-Expose-Headers: <field-name>[, <field-name>]*
Behavior in Presence of Custom Headers
If the cross call contains custom headers, the browser includes those custom headers in the pre-flight invocation:
OPTIONS /blah HTTP/1.1 Host: B.com Access-Control-Request-Method: GET Origin: http://A.com Access-Control-Request-Headers: onecloud-adgroup, onecloud-user Referer: http://A.com/the-page-containinig-js.html
Note the presence of "Access-Control-Request-Headers" in the request.
B.com must acknowledge the headers, otherwise we get an error message similar to:
XMLHttpRequest cannot load http://B.com/blah/. Request header field OneCloud-User is not allowed by Access-Control-Allow-Headers in preflight response.
Acknowledgement is provided by returning an "Access-Control-Allow-Headers" header listing the comma-separated list of allowed headers:
Access-Control-Allow-Headers: Apples, Oranges
Cross-Site Request Forgery (CSRF)
Cookie-to-Header Token.
Web applications that use JavaScript for the majority of their operations may use an anti-CSRF technique that relies on same-origin policy:
1. On login, the web application sets a cookie containing a random token that remains the same for the whole user session
Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
2. JavaScript operating on the client side reads its value and copies it into a custom HTTP header sent with each transactional request
X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
The server validates presence and integrity of the token
Cross-Site Request Forgery and Spring
Testing CORS
test-cors.org
The https://www.test-cors.org page has a "Code" tab that shows the JavaScript code used to send the request. It can be used as a starting point for a more complex CORS test. Also see Testing CORS with a JavaScript Client below.
JavaScript Client to Test CORS
CORS Support in Spring
CORS is supported in Spring with @CrossOrigin.
TODO
See how swagger-generated code implements in io.swagger.api.ApiOriginFilter
:
package io.swagger.api;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JaxRSServerCodegen", date = "2015-09-18T16:59:22.303Z")
public class ApiOriginFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
}
public void destroy() {}
public void init(FilterConfig filterConfig) throws ServletException {}
}