what is a good solution for cross domain access? - cross-domain

What is the most secure way of providing cross-domain access ?
what are the trade-offs ?
I know we can set cross origin access headers, but this would need my server to know the list of servers beforehand.

You do not need to know the list of servers before hand as you can set Access-Control-Allow-Origin: *, however this is not as secure as it will allow other sites to make use of your services. So make sure when constructing your headers that you allow Access-Control-Allow-Origin from a restricted list. I just use a regular expression to compare against since we allow from multiple. When I verify that there is a match, I return the request origin back in the header. So if I match on something like web.*energydomain.com and the origin is webservices.energydomain.com, then I pass back Access-Control-Allow-Origin: webservices.energydomain.com This tells the calling service (and anyone listening) that I accept from this origin and only this origin, even If I might accept from webstart.energydomain.com.
So using spring we have created a filter.
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
response.setHeader("Access-Control-Allow-Credentials", "true")
//If this is a pre-flight request, make sure that we are allowing them
if ("OPTIONS" == request.method) {
response.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
response.setHeader("Access-Control-Max-Age", "604800")
response.setHeader("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Allow-Origin, Vary, Cookie, Key")
//Check to see that the referrer/origin matches the set of allowed origins in the application configuration
String referrer = request.getHeader("Origin")
if (referrer?.matches(ServerProperties.instance.accessControlAllowOriginRegEx)) {
response.setHeader("Access-Control-Allow-Origin", referrer)
}
} else {
//set other headers here and continue chain (we don't bother continuing chain on preflight)
chain.doFilter(request, response)
}
}
You can also do this on htaccess
<FilesMatch "\.(ttf|otf|eot|woff)$">
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(webservices.energydomain.com|webservicesmo.energydomain.com|webservicestest.energydomain.com)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</IfModule>
</FilesMatch>
I prefer attempting to reduce the preflight traffic noise and avoid it all together. In particular, I use xdomain. It was pretty simple for me to setup if using angular or jQuery. On your app server, add a proxy.html as stated in the help on the below link. Add some tags referring to the js files on your "client" and viola, no more pre-flights. This wraps in an iframe to avoid need for cors check. You can still control the origins as you do with the CORS preflights as explained above, it just avoids them alltogether.
https://github.com/jpillora/xdomain

Related

CORS Barrier from subdomain

I use my Web application in a subdomain also, I use a different subdomain for the interface objects.
That's the problem: CORS
Fonts are not installed because of cors barrier.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at
https://assets.example.com/fonts/simple-line-iconsc05f.ttf?thkwh4.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
Application:
https://ap.example.com
Assets:
https://assets.example.com
I added the root of Web application, .htaccess file:
<FilesMatch "\.(ttf|otf|eot|woff|svg|woff2)$">
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
</FilesMatch>
Also, nginx.conf file:
server {
...
location ~* \.(eot|ttf|woff|woff2)$ {
add_header 'Access-Control-Allow-Origin' '*';
}
...
}
Nevertheless, I'm still stuck in the cors barrier.
It has been tried many times with cache and different browsers. The result has not changed.
You can try this:
location / {
if ($request_filename ~* ^.*?/([^/]*?)$) {
set $filename $1;
}
if ($filename ~* ^.*?\.(eot)|(ttf)|(woff)|(jpg)|(png)|(css)|(js)$){
add_header 'Access-Control-Allow-Origin' "*";
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE, PUT';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type,X-Api-Token';
}
}
#Editor, whilst the solution from #Sahipsiz might work, CORS is a complex topic, and as per my previous comment, that solution is technically incorrect (even though it may have solved your symptom, the underlying problem is still there)...
First off, if your browser decides that CORS is in play, it will send the Origin request header, containing the full domain of the requesting page, e.g.:
Origin: https://ap.example.com
If the request doesn't include the Origin request header, then this isn't a CORS request, and you don't need to do anything CORS-related.
If you are sure that you don't need support for cookies to be passed to/from the assets domain, you can simply respond to a CORS request by including this response header:
Access-Control-Allow-Origin: *
However, if you do need cookie support, you'll need to include these two response headers:
Access-Control-Allow-Origin: <value of Origin request header>
Access-Control-Allow-Credentials: true
So in your case, you would respond with this:
Access-Control-Allow-Origin: https://ap.example.com
Access-Control-Allow-Credentials: true
In some cases (which may well not apply to you, since you're just retrieving fonts), prior to making the main GET/POST/PUT/DELETE request, your browser will first make an additional 'preflight' OPTIONS request - this is basically the browser asking permission to make the main request. The OPTIONS request includes a number of CORS-specific request headers, and you need to return some 'matching' CORS response headers to this OPTIONS request (but no response body). If you do this correctly, the browser will then make the main request.
For that OPTIONS request, you should return the following CORS response headers:
Access-Control-Allow-Origin: <value of Origin request header>
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: <value of Access-Control-Request-Method request header>
Access-Control-Allow-Headers: <value of Access-Control-Request-Headers request header>
Those response headers tell the browser that you are OK with the GET/POST/DELETE/PUT request it's about to make.
You can optionally also pass the following response header to the OPTIONS request (in addition to the four defined above):
Access-Control-Max-Age: 86400
which tells the browser to cache the OPTIONS response headers - this stops it from making the preflight request every time.
CORS ain't easy.

Set HTTP Options Response w/ Claudia-Api-Builder for AWS ApiGateway

I am trying to set the Access-Control-Allow-Methods header for options and it appears currently that claudia-api-builder does not have the ability to set the http options response, like a GET request would. See GET example below.
GET Example
api.get('/hard-coded-headers', function () {
return 'OK';
}, {success: {headers: {'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS'}}});
Furthermore ...
If this header value is set via aws-api-gateway -> resources -> OPTIONS > Integration Response and then if you were to perform a claudia update it would be overwritten back to its default state as seen below.
The claudia-api-builder docs show that it supports API Gateway custom error responses but nothing for success.
I would like to be able to set options custom header responses like the way a GET request is handled. Is this possible?
Have you tried the new ApiResponse() function?
api.get('/programmatic-headers', function () {
return new api.ApiResponse('OK', {'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS'}, 200);
});
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to let a user agent gain permission to access selected resources from a server on a different origin (domain) than the site currently in use.
you can guarantee permission to a domain (or several), http verb or contentType
res.header('Access-Control-Allow-Origin', 'example.com');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');

Node-Express-CORS issue

Ionic 2
I am using login provider but when i set the access control to
res.header('Access-Control-Allow-Origin', '*');
It is not working
But it works properly when i use
res.header('Access-Control-Allow-Origin', 'http://localhost:8100');
It is working
but now i want to deploy my app up on phone device i need to set it to wild card res.header('Access-Control-Allow-Origin', '*');. since my app on phone not working on http://localhost:8100 anymore
Anyone can help me solve this problem ?
If you are making a preflighted request then the wildcard is forbidden in the Access-Control-Allow-Origin header.
You can read the Origin request header in order to find out the origin. Then you can test it against a list of allowed origins (you could also assume that any origin is OK, but for a preflighted request there is a good chance that complete public access would be a security risk). Finally you can copy it into the Access-Control-Allow-Origin response header.
How is your HTTP request from your app looks like?
Look for "Types of CORS requests" in this article.
If your HTTP request is a simple one, i.e.
Method is HEAD, GET, or POST
Only have these headers
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type of application/x-www-url-encoded, multipart/form-data, or text/plain
If your HTTP request is a simple one, preflight is not needed. And Access-Control-Allow-Origin with * is accepted by the mobile app.
Otherwise, a preflight request will be made (i.e. OPTION request) and Access-Control-Allow-Origin of * will be ignored. It must be fully specified like http://localhost:8100.

Is there a downside to always returning Access-Control-Allow-Credentials?

A weird CORS question...
I have code in my example.com server which returns the Access-Control-Allow-Origin response header for all POST & GET requests where the Origin request header is passed and it has a value of an example.com sub-domain (superman.example.com, batman.example.com, etc.).
I now need to be able to make AJAX calls passing cookies, so I need to be able to return the Access-Control-Allow-Credentials response header if the request includes cookies.
I could add an additional check to return the Access-Control-Allow-Credentials response header if I see the Cookie request header, but for simplicity, I'm wondering if there is any downside to always returning the Access-Control-Allow-Credentials response header for all GET/POST requests from my sub-domains, where the Origin request header is specified.
Here's my code (it's a Tcl iRule, FWIW):
when HTTP_REQUEST priority 200 {
if { ( [HTTP::method] equals "OPTIONS" ) and
( [HTTP::host] ends_with "example.com"] ) and
( [HTTP::header exists "Access-Control-Request-Method"]) } {
HTTP::respond 200 "Access-Control-Allow-Origin" [HTTP::header "Origin"] \
"Access-Control-Allow-Methods" "POST, GET, OPTIONS" \
"Access-Control-Allow-Headers" [HTTP::header "Access-Control-Request-Headers"] \
"Access-Control-Max-Age" "86400"
} elseif { ( [HTTP::host] ends_with "example.com"] ) and
( [HTTP::header exists "Origin"]) } {
# CORS GET/POST requests - set cors_origin variable
set cors_origin [HTTP::header "Origin"]
}
}
when HTTP_RESPONSE {
# CORS GET/POST response - check cors_origin variable set in request
if { [info exists cors_origin] } {
HTTP::header insert "Access-Control-Allow-Origin" $cors_origin
HTTP::header insert "Access-Control-Allow-Credentials" "true"
}
}
I am aware that if I return the Access-Control-Allow-Credentials response header, I have to specify a named (non-generic) Access-Control-Allow-Origin header (and that may have Vary header issues), but is there anything else I need to be aware of?
If you take defence in depth into consideration, unconditionally including
Access-Control-Allow-Credentials: true
in responses is a bad idea. Your app may indeed be vulnerable to HTTP-header injection. Imagine a situation where the attacker is able to inject—perhaps via a query parameter in the URL—exactly one arbitrary HTTP header in the response. In that case, the attacker would effectively be able to force responses to contain the following headers,
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Credentials: true
which would leave the content wide open to cross-origin attacks from https://attacker.com.
James Kettle mentions something similar in his AppSecUSA 2016 talk, entitled Exploiting CORS Misconfigurations for Bitcoins and Bounties:
It's quite common to find classic HTTP-header-injection vulnerabilities where, for whatever reason, you can't inject into the response content. You can't just inject malicious HTML; the only thing you can do is set HTTP
headers. And CORS offers a brilliant way of exploiting this because you can just say
This content is open to everyone!
using CORS and then attackers can get hold of that [...]

cross domain event source

I am trying to create an EventSource server using nodejs, that will server requests cross domain. I am sending back Access-Control-Allow-Origin header, but the browser (nor Chrome or Opera) won`t let me connect. There are the headers I send back:
this._response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'
});
How can I do this the right way?
Regards
Allow-Credentials cannot be used with Allow-Origin set to *.
Consider writing the received Origin-header in your response.
see https://github.com/Yaffle/EventSource - polyfill can be adopted to support CORS for Firefox, Webkit and IE 8+
Try the following block in place of yours. The browser will call one time with OPTIONS and then the request will be made as expected after that.
You won't need the if statement if you have broken out the request methods - but I wanted to give you a full block just in case you're hosting it like the Hello World example did.
if (req.method === "OPTIONS") {
console.log('!OPTIONS');
var headers = {};
// IE8 does not allow domains to be specified, just the *
// headers["Access-Control-Allow-Origin"] = req.headers.origin;
headers["Access-Control-Allow-Origin"] = "*";
headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
headers["Access-Control-Allow-Credentials"] = false;
headers["Access-Control-Max-Age"] = '86400'; // 24 hours
headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept";
res.writeHead(200, headers);
res.end();
}
You get Security Exception (SECURITY_ERR: DOM Exception 18) if you are using cross domain resources. This could be due to :
trying to access local resources via file:// (local files not served via node server) or
trying to access resources from another server that does not allow CORS or
maybe you are testing the page from the local file instead of from the URL served from your node server.
Note: When responding to a credentialed requests request, the server must specify an origin in the value of the Access-Control-Allow-Origin header, instead of specifying the "*" wildcard.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
So:
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
res.setHeader('Access-Control-Allow-Credentials', 'true')
(server-side)
if client's origin (req.headers.origin) is trusted.
Or simply set {withCredentials: false} (client-side) and use res.setHeader('Access-Control-Allow-Origin', '*') if you don't need it.

Resources