title | source_url | category | version | tags | description | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Cross-origin resource sharing |
Development |
v7.0 |
|
In this article operators and developers may learn about CORS settings in sensenet and how we prevent cross-domain attacks. |
Cross-origin resource sharing (CORS) is a technique that allows client-side web developers to access resources from a different domain. Shared JavaScript files or images are good examples for this. However cross-origin requests can also be used by hackers and malicious sites to access confidential information if a site is not protected against Cross Site Request Forgery (CSRF) attacks. This is why browsers apply strict rules for these operations to prevent hackers from accessing the portal from external sites.
sensenet supports CORS OData requests from version 6.4 and CORS file download requests from version 6.5.3. In this article operators and developers may learn about CORS settings and how we prevent cross-domain attacks.
All the information on this page refers to our OData REST API, as that is the most important entry point for client developers to the repository.
The CORS specification defines two kinds of cross-origin request protocols:
- simple CORS, using a single GET or POST request
- advanced CORS, using a preflight request (supported from sensenet version 6.5.4 patch 8)
For details see the following article:
All OData AJAX requests sent to the portal will receive a response that contains the following http header:
Access-Control-Allow-Origin: <domain>
The domain placeholder above is filled dynamically with the requested origin domain, if it is allowed to access the portal (see next section for details). In case of non-CORS requests it will be the domain that the request was sent to. For example, if you send a GET request to the following URL:
http://example.com/odata.svc/workspaces('myworkspace')
... then the response will contain the following header:
Access-Control-Allow-Origin: http://example.com
When a browser receives the response above, it will allow the JavaScript runtime to access the results only if it was sent from an html page that came from the same domain. E.g. if this AJAX request was made on an html page that was downloaded from a different, malicious website, the request would fail.
Please note that in this case the request was already executed successfully on the server, it is just the client-side JavaScript code that is not allowed to access the results. See the next section about how we use origin check to protect our portals against requests that would cause harm even if the client-side code does not receive the result.
In case of cross-domain requests all modern browsers send the Origin header to the server, containing the domain of the original page. sensenet always checks for the Origin header and if it is different than the requested domain and it is not included in a whitelist, the request will fail on the server without being able to do any harm.
Unlike the old Referer header that contains the whole URL, the Origin header contains only the domain and it cannot be modified after the browser has sent the request, meaning it is reliable.
You can manage CORS-related settings in the following Settings content in the Content Repository
- /Root/System/Settings/Portal.settings
There is a whitelist that contains the trusted 3rd party domains that may send CORS requests to the portal. If a CORS request arrives with an origin that is in this whitelist, the request will execute - otherwise the client will receive a Forbidden status code. You can manage this whitelist the following way:
The list may contain internal or external domains:
{
AllowedOriginDomains: [ "example.com", "www.example.com", "trustedsite.com", "localhost" ]
}
You may define URLs containing a specific port. In this case only the requests arriving from the defined URLs will be allowed: requests from the same domain (localhost
) but without a port will be denied.
If you are using sensenet on URLs containing a port, you will have to define them here with exact port numbers. Simply adding
localhost
is not enough, there is no wildcard support for ports yet.
{
AllowedOriginDomains: [ "localhost:1234", "localhost:5678" ]
}
It is also possible to open the Content Repository to everyone by providing a wildcard as the only allowed origin.
{
AllowedOriginDomains: [ "*" ]
}
from sensenet version 7.0
If you need to, you may customize the list of allowed methods (http verbs) for CORS requests on your sensenet instance. If something is missing from the default list, or you want to restrict the allowed request methods for security reasons, you can do so by providing the following line in the setting:
{
AllowedMethods: [ "GET", "POST", "PATCH", "DELETE", "MERGE", "PUT" ]
}
You can also customize the list of allowed http headers for CORS requests (for example add your custom headers):
{
AllowedHeaders: [ "X-Authentication-Type",
"X-Refresh-Data", "X-Access-Data",
"X-Requested-With", "Authorization", "Content-Type" ]
}
If the client-side JavaScript code tries to make a cross-domain AJAX request with any http method other than GET or POST (e.g. DELETE or PATCH), a preflight request is made to the server using the OPTIONS method to check whether it is allowed to send a CORS request for that particular resource.
Of course cross-domain requests still need to be authenticated. CSRF attacks are designed to make cross-domain calls in the name of a user who is already logged in to the targeted site (e.g. on a different browser tab). Otherwise the whole mechanism described above does not apply because the malicious request will not even reach the point when it would make some damage.
Currently, the portal always allows authenticated requests, except if the allowed origin is a wildcard ("*"). This means that the credentials header is always set to true and browsers will allow ajax requests to send cookies to the server.
Access-Control-Allow-Credentials: true
from sensenet version 7.0
To log in to a portal on a different domain than the current one (the target portal is where you want to send CORS requests), you can use JWT authentication. The easiest way to do that is using the Client JS API instead of implementing the protocol by yourself.
All the protection and protocol above is related to browsers. Web requests made by command line tools do not contain these HTTP headers and the response is not checked by the tool in any way. This means the whole cross-origin protection does not apply to command line tools.
To send a cross-origin request from JavaScript, you simply have to provide the absolute URL of the resource you want to query (e.g. an OData request to a sensenet portal) and tell the browser that you want it to send user credentials with the request:
withCredentials: true
The following examples send a CORS request to a sensenet portal to get memos and create a new one. Of course you will have to be authenticated on the target site to make this work (see authentication section above for details).
// GET request: load memos from the Memos list
$.ajax({
url: "http://example.com/OData.svc/workspaces/Project/madridprojectworkspace/Memos",
xhrFields: {
withCredentials: true
},
dataType: "json",
type: 'GET',
success: function () {
console.log('hello');
},
error: function () {
console.log('error!');
}
});
// POST request: create a memo
$.ajax({
url: "http://example.com/OData.svc/workspaces/Project/madridprojectworkspace('Memos')",
xhrFields: {
withCredentials: true
},
dataType: "json",
type: 'POST',
data: "models=[" + JSON.stringify({ '__ContentType': 'Memo', 'Index': 123 }) + "]",
success: function () {
console.log('hello');
},
error: function () {
console.log('error!');
}
});