Vladimir Dyuzhev
MockMotor Creator
CORS Support
CORS is automatically supported by MockMotor since MockMotor 1.8.2012. You don't have to do anything.
CORS in Mocks
Without a further ado, since version 1.8.2012 you do not need to do anything to enable CORS for any mock service. Every service by default already supports CORS and responds accordingly.
Navigate to the service page and set the Generate CORS Headers
to Auto
:
Want a Finer Control?
For any response you can provide your own values for CORS headers. MockMotor uses your values if it finds them.
Missing CORS response headers are still
auto-generated. For example, here Access-Control-Allow-Origin
is provided by MockMotor, while two other headers have custom values:
What is CORS
The explanation below is simplified. For detailed documentation read Mozilla MDN article.
CORS stands for Cross-Origin Resource Sharing. The name doesn’t make it any clearer what it’s actually for.
Let’s figure it out.
What Problem it Solves
CORS addresses a very common problem in the web. A typical page loads its resources (content, images, scripts, data, text and graphical ads) from a number of sites. Some of those sites can be compromized. These sites then inject spying scripts into the page and these scripts will try and steal data from the other sites under the user id.
For example, a web forum can contain an infected script posted by a user. I visit the forum, my browser downloads and executes the script, and that script then sends requests to a banking site I have visited shortly before. Without a protection mechanism in place the browser amends the script requests with the bank’s cookie permitting the malicious script to pretend it is me and steal.
Same Origin Policy
That risk was countered in 1995 with the Same Origin Policy
.
The Same Origin Policy
states that a page or script can only access the resources loaded from the same site.
“Same site” generally means “same protocol, host and port”.
For example, for a page or script loaded from URL http://example.com:1234/hello
,
these URLs will or will not be considered Same Origin
(i.e. safe):
URL | Same Origin or Not |
---|---|
http://example.com:1234/balance | Yes - same protocol, host and port |
https://example.com | No - HTTPS instead of HTTP |
http://example.com:8080 | No - different port |
http://secure.example.com:1234 | No - different host |
http://site.com:1234 | No - different host |
The Same Origin
rules are not a strict specification and differ even between browsers. For example, for IE only “same protocol and host” are considered.
For the attack described above the failure to pass the Same Origin
check means that the cookie obtained by me on the banking site is not available to the infected script.
The request is unauthenticated and so fails.
However, note that the script is still able to perform the request to the banking site. The request itself is not blocked by the Same Origin
policy, that came later with CORS
.
Cross-Origin Resource Sharing
Here I mostly talk about XMLHttpRequest as it is the primary candidate for mocking.
As the web was becoming more complex and interlinked, more and more sites began actually require cross-site calls. Some web sites were a mashup of data loaded from various data sources and their scripts needed to work with all these sources.
Web was in need of a more flexible (but still secure) way to control access.
The solution was to let the servers (sites) decide who can call them and how.
With CORS, a browser still remembers the origin for all of its loaded resources. However, it doesn’t automatically deny a script an access to another site. Instead, it consults with the target site during or before the script request.
Simple Requests (GET, POST, HEAD)
The script can request to perform a HTTP GET, POST or HEAD call to a not-same-origin server. These HTTP methods are considered simple because they can be performed by other means, too. For example, inserting an <img> tag with a src attribute into DOM will perform a GET.
For these simple cases the browser executes the request immediately. However, it adds a few special HTTP headers to the request. If the response doesn’t contain the expected matching response headers, the response is hidden from the script.
Note that just like with the Same Origin Policy, the request is not blocked. However, its results are not provided to the script if it is not explicitly permitted by the server.
Non-Simple Requests: Pre-Flight with OPTIONS
For other HTTP operations (PUT, DELETE, etc) and for some other more corner cases, the browser performs a pre-flight
check first.
The browser sends a HTTP OPTIONS request to the site the script wants to access. It adds HTTP headers that provide the site with the information about the upcoming call.
If the server doesn’t respond in a way that permits the request, the actual call never happens.
CORS Response
If the site was created without CORS in mind, it will either omit the required CORS headers or even reject the OPTIONS request. The browser then will not allow the script to access the data or to perform the call. This is very good, as the older sites are protected by default.
If the site supports CORS, the HTTP 200 in the response is not enough to permit the call. The server must confirm each CORS header with a matching reply header. Any mismatch and the browser, again, will block the data or the call. This is done to ensure that the site owners have put some thought into the security and would not just reply with HTTP 200 to every CORS call.
The Server is in Control
With CORS, the server can apply a fine-grained control to who can call the server and how. Because the server team knows the best what are the valid uses of their service, they are in the best position to apply the rules that allow for a flexible legitimate use, but stop the attack attempts.
The rules can vary from very strict to very lax.
The mentioned above bank would only allow calls from the same origin, i.e. only from scripts loaded from the bank own site.
A web widget could allow access to client sites but no one else.
A public weather service could allow reading the data to anyone interested.
The main thing is that the access control decision have to be made explicitly. If the site simply ignore CORS, the default access from the browser point of view is DENY.
Examples of CORS Client-Server Exchanges
Simple POST Request
A POST is considered a simple request, and even for cross-domain calls the browser executes it as is. It however adds CORS headers Origin
and Access-Control-Request-*
:
POST http://127.0.0.1:7080/SelfTest/CORSCFG HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Access-Control-Request-Headers: Content-Type, X-Foo
Origin: http://foobar.com/
Access-Control-Request-Method: POST, PUT
Content-Length: 14
Host: 127.0.0.1:7080
{"Hello":true}
If the server permits the call from that origin, it provides the response and the matching response Access-Control-Allow-*
CORS headers:
HTTP/1.1 200 OK
Date: Tue, 30 Oct 2018 02:55:22 GMT
Content-Type: application/json; charset=UTF-8
Access-Control-Allow-Headers: Content-Type, X-Foo
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT
Transfer-Encoding: chunked
{
"status":"OK"
}
Pre-Flight OPTIONS Request
Before executing a cross-domain request with HTTP method other than GET
, POST
or HEAD
, browser sends an OPTIONS
request.
The request has the Origin header (marking the origin of the script/source that executes the request) and Access-Control-Request-*
headers:
OPTIONS http://127.0.0.1:7080/SelfTest/CORSCFG HTTP/1.1
Accept-Encoding: gzip,deflate
Access-Control-Request-Headers: Content-Type, X-Foo
Origin: http://foobar.com/
Access-Control-Request-Method: PUT, POST, DELETE, OPTIONS, GET
Host: 127.0.0.1:7080
If the server is OK with such a request, it provides the matching response Access-Control-Allow-*
CORS headers:
HTTP/1.1 204 No Content
Date: Tue, 30 Oct 2018 02:51:14 GMT
Content-Type: text/xml; charset=UTF-8
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: PUT, POST, DELETE, OPTIONS, GET
Access-Control-Allow-Headers: Content-Type, X-Foo
The browser then executes the actual request (still adding CORS headers to it, too!):
PUT http://127.0.0.1:7080/SelfTest/CORSCFG HTTP/1.1
Accept-Encoding: gzip,deflate
Access-Control-Request-Headers: Content-Type, X-Foo
Origin: http://foobar.com/
Access-Control-Request-Method: PUT, POST, DELETE, OPTIONS, GET
Host: 127.0.0.1:7080
Content-Length: 14
{
"feedback":"Accepted"
}