CORS $.ajax session cookies (access-control-allow-credentials & withCredentials=true) - ajax

I realize this question has been asked a dozen or more times and each response given indicates I am doing it right but perhaps I am missing something.
AJAX serves up CORS request like so...
$.ajax({
url: 'someotherdomain.com',
type: 'post',
data: {key: 'value'},
dataType: 'json',
async: false,
crossDomain: true,
beforeSend: function(xhr){
xhr.withCredentials = true;
},
success: function(x, status, xhr){
},
error: function(xhr, status, error){
}
});
PHP serves up CORS requests like so...
header('Access-Control-Max-Age: 1728000');
header('Access-Control-Allow-Origin: http://someotherdomain.com');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-MD5, X-Alt-Referer');
header('Access-Control-Allow-Credentials: true');
header("Content-Type: application/json; charset=utf-8");
According to all documentation as long as the 'Access-Control-Allow-Credentials' server side header, and the 'withCredentials=true' client side header is set session cookie handling between the domains should be transparent. Am I missing something?

async: false
was preventing the session cookie from being sent back to the server on each request. The following fixed it.
async: true
Although this does allow for the session cookie to get set by the browser when making a cross origin request sharing call, I am now experiencing problems regarding the following scenario:
Server A sends response to client
Client using CORS makes request of server B
XMLHttpRequest -> PHP -> Session handler -> MySQL -> Stored Procedure
Due to the MUTEX locks in the PHP session management the asynchronous nature and apparently, requirement may force a work around of manually setting the cookie with a different header option such as XCookie or something similar to keep the servers session and client requests synchronized.
This particular work around does not sit well with me as I believe it would open up an easy lane of travel for session hijacking and session replay attack vectors.
Using an SSL/TLS wrapped connection may assist in preventing the above scenario but in terms of independently providing security measures for the client I do not believe this should suffice.
Anyone with any thoughts on this?

In your example above, you are setting the Access-Control-Allow-Origin header to 'http://someotherdomain.com', which is the same as the url you are requesting from JQuery. The Access-Control-Allow-Origin header should be the value of the domain the request is coming from. As a quick, test, try setting the value of this header to '*' (without the quotes) and see if it works ('*' means all domains are allowed).

Related

Firefox not sending cookie in Ajax request (XMLHttpRequest)

I'm having a new problem with a cross-site JavaScript request and sending cookies, but only in Firefox.
I have this code
$.ajax({
type: "POST",
url: "https://server.com/page",
data: $('#formdata').serialize(),
xhrFields: {
withCredentials: true
},
success: function(html){
alert("Thank you for your submission")
},
crossDomain: true
});
}
(context: it is in an example of how CSRF attacks work)
https://server.com/page relies on a session ID cookie to be sent. The cookie has been set, with SameSite set to None and the Secure flag included. myserver.com has the Access-Control-Allow-Origin set to include the site making this request. It also has
Access-Control-Allow-Credentials: true
However, in Firefox, the session ID cookie doesn't get sent. I am sure it used to before my most recent Firefox upgrade. I checked in a different tab and the cookie is there, and does have the correct settings.
It works in Chrome and it works in Safari.
Does anyone know if Firefox has changed its policy recently? Is there a setting I can configure?
My Firefox version is 104.0.2 (64 bit) and I am running on Mac (Monterey).
Never mind, I found it. Firefox's Total Cookie Protection prevents pretty much all cross-site cookie sending. Arguably a good thing but if, like me, you do want to enable cross-site cookies, go to Settings in Firefox and Privacy & Security. Under Enhanced Tracking Protection, click on Custom and uncheck Cookies.

Ajax saving but not sending CORS cookies from 127.0.0.1 to service

So I've been having issues sending cookies with a cross-domain request to a service. I've gotten to make it work in our CI environment, but not locally. Basically, I have an API at api.service.com, and it's accessed via AJAX calls run from clients at webapp.service.com. The API sets a cookie for .service.com via set-cookie. Then all subsequent calls to the API should include this cookie. This works as intended, when running from webapp.service.com. This will work in prod just fine. However, for obvious reasons, I'd like be able to develop the webapp locally, and run API calls against api.service.com from either local files or localhost service.
I understand that Chrome is a little iffy regarding saving cookies for local files, but I've addressed that, and it is not the issue. The cookie is, in fact, saving. It's just not sending that cookie with subsequent API calls. Here's the workflow I've got going on (with some genericized/censored product names):
An AJAX call POSTS to our API:
$.ajax({
method: "POST",
crossDomain: true,
xhrFields: {
withCredentials: true,
},
url: 'https://api.service.com/login',
data: data,
contentType:"text/plain",
dataType: "json",
success: function(data){
...
}
});
The CORS stuff is set up to allow credentials and this origin (the allowed origin updates dynamically, doesn't use *. So we get this cookie back: cookie:service-token=7f7d251ebeec37f7c0815....; SameSite=lax;Max-Age=2629744; domain=.service.com; path=/;
It shows up in Chrome like this:
Request cookie
I know for a fact that this actually works to save the cookie. However, perhaps not how I want. I go into Chrome's cookies, and it updates properly as seen:
Chrome saved cookie
The problem I'm seeing there is that its "send for" value is "same-site connections only". I have no idea how to originally set that for Chrome to treat it as "any kind of connection." I think this is the reason that, when I send another AJAX call, that cookie is not included in the request.
I've seen other posts like this that were resolved by adding crossDomain and/or withCredentials to the AJAX call. This did not resolve it for me. This is a subsequent call to the API:
$.ajax({
url: 'https://api.service.com/getTheThing',
crossDomain: true,
xhrFields: {
withCredentials: true
},
success: function(data){...}
});
The cookie is not included in this request, and thus fails.
It turns out, setting SameSite=lax was doing the opposite of what I thought it would. Removing that solved this issue.

CORS could not get fixed even after adding Access-Control-Allow-Origin in service

I tried to access a Rest service which was hosted in different domain from mine through an ajax call and I got "CORS" error in the firebug.
After researching about this problem, I figured out that the service needs to be changed by adding Access-Control-Allow-Origin to * in the response header. I did that in the service as well.
public Response search(String expression) {
return Response.ok() //200
.entity(ConnectionUtils.query(expression))
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.header("Access-Control-Allow-Headers", "Content-Type,Accept")
.allow("OPTIONS").build();
}
Above method is the implementation class of below service interface:
#POST
#Path("/search")
public Response search(String expression);
I tried to post a request to this url through chrome advanced rest client, I am getting the response as well. Also the response header shows that the Access-Control-Allow-Origin has been set properly as well. Please refer to below screenshot of chrome client:
If you see above, the response headers has been changed.
But my below ajax call always return CORS error:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://192.168.1.100:8080/cqs-1.0-SNAPSHOT/services/services/cqs/search. This can be fixed by moving the resource to the same domain or enabling CORS."
$.ajax({
type: 'POST',
url: "http://192.168.1.100:8080/cqs-1.0-SNAPSHOT/services/services/cqs/search",
crossDomain: true,
dataType: 'json',
data: {"offer.offer.offerId.USSellerId": {$gt: 0}},
headers: {
"Accept": "application/json",
contentType: "application/json"
},
success: function (data) {
console.log(data);
},
error: function (data) {
console.log(data.statusText);
console.log(data.name);
}
});
Don't use CORS. It's fraught with peril. Rather, set up a simple reverse proxy on the server using Apache (or similar). Once you set it up, all requests from your app can go back to your domain/port thus not triggering any OPTION methods from the browser. Meanwhile your reverse proxy can redirect requests based on the path in the url, which is completely up to you on how you want to configure.
In my case, I just had a simple case of needing to access 2 different ports on the same server. Port 8080 (tomcat) was serving my GWT UI and REST requests from my GWT pages (using RestyGWT) were needing to hit port 9000 (Play framework port). Due to the different ports, CORS was required to deal with the OPTION 'preflight' checks that the browser was doing.
To solve this I just setup my URLs as having either /ui path or a /api path.
Since the domain/port was the same, and my proxy could easily redirect transparently to the correct port (api -> 9000 and ui -> 8080) there was no longer any need for CORS.
CORS has a lot of issues in my experience including cookies. It's really better to avoid it using a reverse proxy.
If you need more details, I can post more - let me know.
JR

Cross-domain AJAX POST withCredentials and IE8+ compatibility

I have a login setup for one of my sites where the user types their information into a login popup on the home page, which then submits the information back to a servlet and then receives a response back via JSON. The home page then proceeds to send the user to their profile page or alternatively displays an error (e.g., if username and password do not match).
$.ajax({
dataType: 'jsonp',
async: false,
url: loginLocation,
type: 'GET',
crossDomain: true,
cache: false,
xhrFields: crossDomain ? {
withCredentials: true
} : {},
data: ({'key1': value1, 'key2': value2, ..., 'keyN':'valueN'}),
success: function(data){
if (data && data.status && data.status == "success") {
window.location = profileLocation;
} else {
errorHandler();
}
},
error: errorHandler
});
I am looking to change this from a GET request to a POST in order to prevent arbitrary query strings being sent into the servlet. However, it appears that there are several considerations at play here with regards to how the solution ought to be laid out. It must:
use POST instead of GET
be a cross-domain request (the login page and the servlet are on different domains over both of which I have access/control)
use the withCredentials parameter (the login functionality relies on the JSESSIONID cookie so this parameter is required)
be compatible with IE8 and above
I have tried looking into cross-domain ajax requests that fit the above criteria, but the major sticking point seems to be the IE8/IE9 compatibility. Approaches such as easyXDM appear to be ambiguous as to support for these browsers (I have seen conflicting reports online as to how it works in IE8) and I don't want to run into the danger of realizing it won't work halfway through implementation.
So in short, is there a way to do cross-domain ajax requests using POST and with the withCredentials parameter, that is also compatible with IE8+? Is easyXDM an appropriate solution to this?
I was able to determine the solution to the above question by using the xdomain library (found at https://github.com/jpillora/xdomain) which overrides the request behavior to allow cross-domain ajax in IE8 and IE9. This involved setting up the proxy.html as shown in the example on the xdomain site as well as adding Access-Control-Allow-Origin and other related headers to the server response. This allows cross-domain ajax JSON POST requests using withCredentials in IE8+ per the criteria listed in the original post. It also allows cross-domain requests between HTTP and HTTPS.

Ajax cross domain request allowed in internet explorer

I've been asked to create a feedback page that can be requested from another site.
I'm using progressive enhancement to display the page.
The ajax request for when I am able to use a jquery dialog is as follows
jQuery.support.cors = true;
$.ajax({
type: 'get',
crossDomain: true,
url: this.href
}).done(function (data) {
$dialogFeedback.html(data);
}).error(function (jqXHR, textStatus, errorThrown) {
$dialogFeedback.html(jqXHR.responseText || textStatus);
});
During testing I have noticed Internet explorer seems to be allowing a cross domain call even when the response Access-Control-Allow-Origin HttpHeader is not set to be the client domain. I've noticed the Http origin header is always null.
Chrome and Firefox respect it. The Http origin header is not null.
The client site making the call is on a different port to the feedback site but both are localhost. I have read that a different port number is considered cross domain.
At the moment I find myself having to retrieve the caller/client domain from the Referrer Http header and returning a 404 if the domain is not known by us.
Really I was hoping to rely on the Access-Control-Allow-Origin HttpHeader!
. . . so my question is why is this happening? Is it actually expected/probable? What is the best solution?
Thanks
If the origin header is missing, then the request is not cross-domain according to IE. IE violates the same-origin policy RFC in several ways. First, it ignores port numbers. Second, IE will allow domains that are in the trusted zone to interact without applying the same origin policy

Resources