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

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 [...]

Related

Cookies disappear after redirect

I have:
1) A client side app that has its own domain: http://client.com
2) A server side app that has a separate domain: http://server.com
Now,
the scenario is:
1) Opening http://client.com/home in the browser, which displays an HTML page.
2) http://client.com/home redirects to http://server.com/login
3) http://server.com/login stores a cookie 'auth' and sends a redirect instruction to http://client.com/welcome
Response:
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 104
Content-Type: text/html; charset=utf-8
Date: Wed, 16 Jan 2019 10:47:11 GMT
Location: http://client.com/welcome
Set-Cookie: auth=1479da80-197c-11e9-ba74-59606594e2fb; Path=/
Vary: Accept
X-Powered-By: Express
4) The browser receives the response, which does contain the cookie 'auth'
5) The browser redirects itself to http://client.com/welcome
6) 'auth' cookie is sent to http://client.com/welcome
Request:
Cookie: auth=1479da80-197c-11e9-ba74-59606594e2fb
7) http://client.com/welcome returns HTML but does not return the cookie 'auth'
8) http://client.com/welcome makes an AJAX request to http://server.com/data (CORS enabled), but the cookie 'auth' is not sent
9) http://server.com/data doesn't recognize the user because there is no cookie
The client side is an angular app hosted by Node.js
Edit:
As suggested, I've added to the response of server.com:
Access-Control-Allow-Credentials: true
but nothing has been changed.
Relevant client side code:
const headerOptions = new HttpHeaders({
'Content-Type': 'application/json', 'withCredentials': 'true', 'Access-Control-Allow-Origin': 'true', 'Access-Control-Allow-Credentials': 'true'
});
this.httpClient.get<any>(this.baseUrl + "data", { headers: headerOptions }).subscribe((res) => {
You should use the withCredentials option when sending your ajax request to your http://server.com and your server.com should have the Access-Control-Allow-Credentials set to true.
Example code in Node.JS server:
var cors = require('cors');
var corsOptions = {
origin: '*',
credentials: true };
app.use(cors(corsOptions));
More on this here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
Example code in Angular.JS client
import {RequestOptions, Request, RequestMethod} from '#angular/http';
const options = new RequestOptions({
method: RequestMethod.Post,
url: 'https://google.com',
withCredentials: true
});
More on this here: https://angular.io/api/http/RequestOptions
Also check this out: https://github.com/angular/angular/issues/24283 - it looks like a particular version of Angular had problems with this flag, so unless you're using a more up-to-date version, you might need to set the header explicitly.
The reasoning of this is, unless the server explicitly tells the client "I will accept cookies (set previously on my domain) passed by another domain" - accepting the cookies would be a security issue. More here: https://en.wikipedia.org/wiki/Cross-site_request_forgery
Your description of what is happening does not seem right.
http://server.com/login stores a cookie 'auth' and sends a redirect instruction to http://client.com/welcome
'auth' cookie is sent to http://client.com/welcome
That is not (or at least should not be) what is happening. When the browser requests http://server.com/login and gets back in the response a Set-Cookie header, the cookie is set on and restricted to the server.com domain, even if the response is a redirect. If you are seeing the 'auth' cookie sent to client.com then that is a cookie that client.com previously set.
Anyway, it seems that what you really care about is
http://client.com/welcome makes an AJAX request to http://server.com/data (CORS enabled), but the cookie 'auth' is not sent
There are a bunch of reasons this can happen.
CORS. You mentioned it was CORS enabled, but for the sake of others reading this, you must have the following CORS headers set on server.com
Access-Control-Allow-Origin: http://client.com
Access-Control-Allow-Credentials: true
Note that you cannot get away with using a wildcard for Access-Control-Allow-Origin when you are sending credentials. Also note that the origin has to be an exact match, including scheme (http or https). In practice, what servers generally do is read the Origin header of the request, check it against a white list, and if it allowed, copy the Origin header value from the request to the Access-Control-Allow-Origin header in the response.
You must set xhr.withCredentials = true in your XHR request. (See MDN for more details.)
Then after you have done all that, you have one other hurdle in your way. Because you are on client.com and trying to send a cookie to server.com, the server.com cookie is considered a "third-party" cookie. AFAIK all the major browsers have a setting that blocks third-party cookies for privacy, because they are most often used by trackers to gather marketing data for advertising. I believe most of them block third-party cookies by default, but I am not sure of that. For sure lots of people have set their browsers to block third-party cookies.
So you have to tell your visitors to configure their browser to allow third-party cookies from server.com.
BTW, it is not safe to set a cookie on a redirect to a different domain. While it is allowed under the specification AFAIK, there have been issues with browser support. See, for example, this Chrome bug.
From Access-Control-Allow-Origin spec:
For requests without credentials, the literal value "" can be specified, as a wildcard;*
Try to add specific domain to Access-Control-Allow-Origin field.
I think you should use proxy in angular angular app. For more info check this link: https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md

How does CORS work in AWS, NodeJs and other web services? [duplicate]

This question already has answers here:
How does the 'Access-Control-Allow-Origin' header work?
(19 answers)
Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API?
(36 answers)
Closed 4 years ago.
I was working on a AWS project, and noticed that in order to allow CORS, my seniors had set headers property Access-Control-Allow-Origin, in response. So whole response was like:
module. exports.handlerFunction = async ( event, content) => {
// code here for DB calls and other logic
return {
headers: {
"Access-Content-Allow-Origin" : "*"
},
statusCode: 200,
body: result
};
}
And my thoughts were, how is it even working and allowing CORS?. What if we didn't wanted origin to perform any operations and had done "Access-Content-Allow-Origin": "https://example.com".
Since we are setting this in response, so the origin which was not supposed to do anything and just return, now have done everything and then responded with error of CORS. I asked my seniors, "How this is working and How CORS work?", the response was its browser property and browser send a pre-flight request and check for CORS. But we check for CORS at end, once every thing is done, how did pre-flight request skipped all our checks DB and API call and just landed at end and check for response headers. They had no answer and said same pre-flight concept. I asked next question "browser have pre-flight concept to check for CORS, what about postman, cUrl requests and then API call via various programs like node-fetch, request, https API call module in NodeJs do they also make pre flight call".
Also when I was creating my NodeJs express Server application, I used cors.js a NPM module. With that I checked for CORS before entering into any API function, on entry of every call and only allowed permitted source to enter. Code is like:
const CORS = require('cors'),
express = require('express');
const app = express();
let allowedOrigin = ['https://example.com'];
let corsOps = {
origin: (origin, cb) => {
if (allowedOrigin.includes(origin))
cb(null, true);
else
cb(new Error('Not allowed'));
}
};
app.use(CORS(corsOps));
This checked before calling any function and not on response.
I searched a lot about this behavior and have seen multiple examples of using CORS in headers, How does it even work in headers?.
For me it's my backend that stops call and check who is calling backend API.
How can someone who is making requests set property in headers and backend open its access to anyone, just by seeing headers property and not checking source that called?
When browsers execute an AJAX call, they first check if the target origin matches the current origin a.k.a. window.location.origin. If not, they check if the target origin accept window.location.origin as source of a CORS request. If not, the browser shows the infamous No 'Access-Control-Allow-Origin' header is present on the requested resource. error message.
If the request can be made, the browser includes the Origin header in the HTTP request with the value of window.location.origin.
There are multiple ways to set up the CORS policy in the backend:
Accept all origins with the wildcard
Only accept a preconfigured set of origins
Dynamically take the value of the Origin header as the value of the "Access-Content-Allow-Origin on a request by request basis
In the last case, the origin may be obviously spoofed like so:
curl -H"Origin: https://www.example.com" https://api.example.com/some/path
Whether or not a server should serve a request should not obviously depend on the Origin header but on other factors such as an Authorization header or an appropriate authorization Cookie; validity of request parameters etc.

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.

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.

what is a good solution for cross domain access?

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

Resources