Cant set cookie for remote domain with AJAX (CORS) - php

I have two domains:
login.com and site.com
On the page login.com/ajax.htm I have the following code:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script>
$(function() {
$.get('http://site.com/setCookie.php', {}, function(text) {alert(text);});
});
</script>
setCookies.php on site.com contains only:
header('Access-Control-Allow-Origin: *');
setcookie('Cookie', 'Cookie', time()+60*60*24*30, '/', 'example.com');
setcookie('Cookie3', 'Cookie3', time()+60*60*24*30, '/', '.example.com');
setcookie('Cookie4', 'Cookie4', time()+60*60*24*30, '/', '');
setcookie('Cookie5', 'Cookie5');
echo 'Cookies set';
All I want is that setCookies.php will set cookies for site.com, when requested from ajax.htm.
Actually, when I visit login.com/ajax.htm, I get JS alert "Cookies set", but none of them are set, in fact :)
So the question is, how I can make a script set cookie for its own domain (not for the domain from which its being requested), when it is requested with AJAX?

Related

Set a cookie on another domain using ajax and php - FAILS

Domain-Being-Browsed.com has a javascript function, which when triggered makes an ajax call to a php file on another domain: another-domain.com/set-cookie.php
set-cookie.php contains code like this:
header("Access-Control-Allow-Origin: http://localhost:3000");
header("Access-Control-Allow-Credentials: true");
$cookie_set = FALSE;
$cookie_set = setcookie( 'cookie_name', 'cookie_value', $a_big_number, '/' );
if( $cookie_set ){ echo 'cookie set!'; }
The javascript function is like this:
var url = 'http://another-domain.com/set-cookie.php';
$.ajax({
'url': url,
'xhrFields': {
withCredentials: true
}
}).done(function(resp){
console.log(resp);
});
If I visit http://another-domain.com/set-cookie.php in my browser, it sets the cookie.
If I trigger the javascript function, I get a response in the console, 'cookie set!', but when I load http://another-domain.com in my browser, I find that the cookie is not there.
I found this post: Can't set cookie on different domain, which seems exactly like mine, but the answer given I've incorporated already and it doesn't help. What am I missing?
As directed by Barmar, I checked the Network tab in dev tools for the Set-Cookie header in the response. It seems that I needed to set 'SameSite' to 'None'. If you set 'Samesite' to none, you have to set 'secure' also. And if you set secure, you have to load the page over https. So the answer, as found in the answer here: How to fix "set SameSite cookie to none" warning?
is to change the php file like this:
header("Access-Control-Allow-Origin: http://localhost:3000");
header("Access-Control-Allow-Credentials: true");
$cookie_options = array(
'expires' => time() + 60*60*24*30,
'path' => '/',
'secure' => true,
'samesite' => 'None'
);
$cookie_set = FALSE;
$cookie_set = setcookie( 'cookie_name', 'cookie_value', $cookie_options );
if( $cookie_set ){ echo 'cookie set!'; }
and to load the page over https. Works!

How to create cookie or session through jquery ajax?

I'm trying to build own javascript API for a chat online. This API is intended to get css file and javascript file and after loading the website it must create a conversation window. I want to share this API for other users.
e.g. If my domain is mydomain.com and includes in html code my javascript API, which downloads css file and javascript file from example.com, what is the best practice to create cookie or session on the user side?
Below is my API:
<div id="fb-dialog"></div>
<script async defer src="http://coders.localhost/api/fb-dialog.js">
var apiConf = {
key: 'YOUR_API_KEY',
language: {
/* Your language codes and default greeting */
"pl" : "Dzień dobry, jestem online. W czym mogę pomóc?",
"en" : "Hello, I'm online. How can I help you?"
}
}
</script>
I want share this chat but any conversation must be save on my server.
I want to use a database which will to have a website's ID on the which is installed the API. Before starting a conversation in cookie I want get website ID on the which can I will return response. How can I to do?
My javascript:
<script>
$.ajax({
xhrFields: {
withCredentials: true
},
type: "GET",
url: "http://coders.localhost/modules/fb-dialog/heading.php"
}).done(function (data) {
alert("OK");
});
</script>
My PHP script:
<?php
if (!isset($_SERVER['HTTP_ORIGIN'])) {
// This is not cross-domain request
exit;
}
$wildcard = false; // Set $wildcard to TRUE if you do not plan to check or limit the domains
$credentials = true; // Set $credentials to TRUE if expects credential requests (Cookies, Authentication, SSL certificates)
$allowedOrigins = array('http://firma.localhost', 'http://jsfiddle.net');
if (!in_array($_SERVER['HTTP_ORIGIN'], $allowedOrigins) && !$wildcard) {
// Origin is not allowed
exit;
}
$origin = $wildcard && !$credentials ? '*' : $_SERVER['HTTP_ORIGIN'];
header("Access-Control-Allow-Origin: " . $origin);
if ($credentials) {
header("Access-Control-Allow-Credentials: true");
}
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Origin");
header('P3P: CP="CAO PSA OUR"'); // Makes IE to support cookies
// Handling the Preflight
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit;
}
if(!isset($_COOKIE['fb-dialog'])) {
setcookie("fb-dialog", "true", time()+3600, "/");
}else{
if(!isset($_POST['data'])) {
if($_COOKIE['fb-dialog'] == 'true') {
setcookie('fb-dialog', 'false', 0, '/');
} else {
setcookie("fb-dialog", "true", time()+3600, "/");
}
}
}
// Response
header("Content-Type: application/json; charset=utf-8");
echo json_encode(array('status' => 'OK'));
?>
The above code is working. I'm getting a status 200 but a cookie is not for proper domain because the script create a cookie for domain coders.localhost
I have two domains:
http://firma.localhost - this is my website
http://coders.localhost - this is remote domain from who I get API
I want create cookie by coders.localhost domain for firma.localhost.
You can also use plain javascript:
document.cookie = "username=admin";
or
var x = document.cookie;
Probably you have figured already the answer, It would be great if you shared how you resolved it.
I think the issue maybe due to your / configuration in the setcookie
Your config:
setcookie("fb-dialog", "true", time()+3600, "/");
Try this:
setcookie("fb-dialog", "true", time()+3600, "http://coders.localhost");

403 Forbidden Access to CodeIgniter controller from ajax request

Im having trouble with sending an ajax request to codeigniter controller. It is throwing back a 404 Forbidden Access error. I have found some sort of similar question to this but im not sure if its particular to CodeIgniter framework, and also the solution give in that thread did not solve my problem. below is my ajax request. Im wondering this is probably because of the .htaccess of the root folder of CI Application folder, but i dont want to change its default configuration yet.
Is sending ajax request to CI controller the correct way of implementing this? if not, any suggestion please. Thanks!
var ajax_load = '{loading gif img html}';
var ajax_processor = 'http://localhost/patientcare-v1/application/controller/ajax_processor/save_physical_info';
$("#save").click(function(){
$("#dialog-form").html(ajax_load);
$.post(
ajax_processor,
$("#physical-info").serialize(),
function(responseText){
$("#dialog-form").html(responseText);
},
"json"
);
});
CodeIgniter use csrf_protection, you can use it with Ajax and JQuery simply.
This (ultimate ?) solution work on multiple Ajax request (no 403 ;-) and preserve the security).
Change the configuration
Open the file /application/config/config.php
and change the line $config['csrf_token_name'] by :
$config['csrf_token_name'] = 'token';
You can use another name, but change it everywhere in future steps.
Add CSRF in your Javascript
Add script in a view; for me is in footer.php to display the code in all views.
<script type="text/javascript">
var CFG = {
url: '<?php echo $this->config->item('base_url');?>',
token: '<?php echo $this->security->get_csrf_hash();?>'
};
</script>
This script create an object named CFG. This object can be used in your Javascript code. CFG.url contain the url of your website and CFG.token ... the token.
Automatically renew the CSRF
Add this code in your part $(document).ready(function($){---}) as
$(document).ready(function($){
$.ajaxSetup({data: {token: CFG.token}});
$(document).ajaxSuccess(function(e,x) {
var result = $.parseJSON(x.responseText);
$('input:hidden[name="token"]').val(result.token);
$.ajaxSetup({data: {token: result.token}});
});
});
This script initialize the CSRF token and update it everytime when a request Ajax is sended.
Send the CSRF in PHP
I've created a new controller, named Ajax. In CodeIgniter, the link to use it is http://www.domain.ltd/ajax/foo
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Ajax extends CI_Controller {
public function foo() {
$this->send(array('foo' => 'bar'));
}
private function send($array) {
if (!is_array($array)) return false;
$send = array('token' => $this->security->get_csrf_hash()) + $array;
if (!headers_sent()) {
header('Cache-Control: no-cache, must-revalidate');
header('Expires: ' . date('r'));
header('Content-type: application/json');
}
exit(json_encode($send, JSON_FORCE_OBJECT));
}
}
The send function add the CSRF automatically and transform an array in object.
The final result
Now, you can use Ajax with JQuery very simply !
$.post(CFG.url + 'ajax/foo/', function(data) {
console.log(data)
}, 'json');
Result :
{"token":"8f65cf8e54ae8b71f4dc1f996ed4dc59","foo":"bar"}
When the request get data, the CSRF is automatically updated to the next Ajax request.
Et voilà !
Remove the <code> and application/controller from your ajax_processor like,
var ajax_processor = 'http://localhost/patientcare-v1/index.php/ajax_porcessor/save_physical_info';
If you are hiding index.php from url by using htaccess or routing then try this url,
var ajax_processor = 'http://localhost/patientcare-v1/ajax_porcessor/save_physical_info';
I was facing same problem but now I have fixed this problem.
First of all, I have created csrf_token in header.php for every pages like below code
$csrf = array(
'name' => $this->security->get_csrf_token_name(),
'hash' => $this->security->get_csrf_hash()
);
<script type="text/javascript">
var cct = "<?php echo $csrf ['hash']; ?>";
</script>
After that, when we are sending particular value through ajax then we will have to sent csrf token like below code
$.ajax({
url:"<?php echo APPPATHS.'staff_leave/leaveapproval/getAppliedLeaveDetails'; ?>",
data:{id:id,status:status,'<?php echo $this->security->get_csrf_token_name(); ?>': cct},
method:"post",
dataType:"json",
success:function(response)
{
alert('success');
}
});
I hope this code will help you because this is working for me.
// Select URIs can be whitelisted from csrf protection (for example API
// endpoints expecting externally POSTed content).
// You can add these URIs by editing the
// ‘csrf_exclude_uris’ config parameter:
// config.php
// Below setting will fix 403 forbidden issue permanently
$config['csrf_exclude_uris'] = array(
'admin/users/view/fetch_user', // use ajax URL here
);
$('#zero-config').DataTable({
"processing" : true,
"serverSide" : true,
"order" : [],
"searching" : true,
"ordering": false,
"ajax" : {
url:"<?php echo site_url(); ?>admin/users/view/fetch_user",
type:"POST",
data: {
},
},
});

Specify Multiple Subdomains with Access Control Origin

I am trying to allow access to every subdomain on my site in order to allow cross subdomain AJAX calls. Is there a way to specify all subdomains of a site like *.example.com or alternatively, why does the following not work when I have more than one domain listed:
header('Access-Control-Allow-Origin: http://api.example.com http://www.example.com');
I have read through the following question which appears to be similar, if not the same as this one, other than the fact that I want access to subdomains and this one refers to general domains.
Access-Control-Allow-Origin Multiple Origin Domains?
If the above question is the solution to this problem, then how am I able to retrieve the origin from the header. It appears that $_SERVER['HTTP_ORIGIN'] is very unreliable and not even cross browser. I need to be able to see the origin in any browser that may show an error when trying to send an AJAX call using javascript.
The solution to this issue is to use the $_SERVER['HTTP_ORIGIN'] variable to determine whether the request has come from an allowed domain, and then conditionally set the Access-Control-Allow-Origin like so:
$allowed_domains = [/* Array of allowed domains*/];
if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_domains)) {
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
}
Here's how I did it.
The Origin header is specified by the browser and will contain the domain that requested the script on the other domain:
Origin: http://www.websiteA.com
Therefore you can "whitelist" multiple domains in your server-side script:
$allowedOrigins = [
"http://www.websiteA.com",
"https://www.websiteB.com"
// ... etc
];
What you can then do is check if the $_SERVER["HTTP_ORIGIN"] global contains a domain within that whitelist:
if (in_array($_SERVER["HTTP_ORIGIN"], $allowedOrigins)) {
And set the Access-Control-Allow-Origin response header to whatever Origin header value was:
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
Full script:
$allowedOrigins = [
"http://www.websiteA.com",
"https://www.websiteB.com"
// ... etc
];
if (in_array($_SERVER["HTTP_ORIGIN"], $allowedOrigins)) {
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
}
While the answer works, it does defeat the purpose of the whole thing, since it allows requests from any host.
I use something like:
if(isset($_SERVER['HTTP_ORIGIN'])) {
$origin = $_SERVER['HTTP_ORIGIN'];
if($origin == 'https://sub1.my-website.com' OR $origin == 'https://sub2.my-website.com') {
header("Access-Control-Allow-Origin: $origin");
}
}
If you want wildcard domain, i think this is more efficient
if(isset($_SERVER['HTTP_ORIGIN']) && preg_match('!^http(s)?://([a-z0-9\-]+\.)?example\.com$!is', $_SERVER['HTTP_ORIGIN']))
{
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
}
I tried using this approach to achieve constraint on a specific domain basis:
$allowed_origin = '';
$parts = explode('.', parse_url($_SERVER['HTTP_HOST'])['host']);
if(end($parts).".".prev($parts) === "com.domain") {
$allowed_origin = $_SERVER['HTTP_ORIGIN'];
header('Acesss-Control-Allow-Origin: '. $allowed_origin);
}
I hope it works.
//Function to be called first in php file.
function CORS_HEADERS_HANDLER(){
if (isset($_SERVER['HTTP_ORIGIN'])){
switch($_SERVER['HTTP_ORIGIN']){
//Handle an IP address and Port
case 'http://1.2.3.4:4200':
header('Access-Control-Allow-Origin: http://1.2.3.4:4200');
break;
//Handle an Website Domain (using https)
case 'https://www.someSite.com':
header('Access-Control-Allow-Origin: https://www.someSite.com');
break;
//Handle an Website Domain (using http)
case 'http://www.someSite.com':
header('Access-Control-Allow-Origin: http://www.someSite.com');
break;
//Catch if someone's site is actually the reject being cheeky
case 'https://not.you':
header('Access-Control-Allow-Origin: https://nice.try');
break;
//Handle a rejection passing something that is not the request origin.
default:
header('Access-Control-Allow-Origin: https://not.you');
break;
}
}else{
header('Access-Control-Allow-Origin: https://not.you');
}
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
header('Access-Control-Allow-Credentials: true');
header('Content-Type: application/json; charset=utf-8');
header("Cache-Control: public,max-age=3600");
//if its an options request you don't need to proceed past CORS request.
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
die();
}
}
This was my challenge and solution:
1 - Backend PHP on api.example.com.
2 - Multiple JS front ends such as one.example.com, two.example.com etc.
3 - Cookies needed to be passed both ways.
4 - AJAX call from multiple front-ends to PHP backend on api.example.com
5 - In PHP, I do not prefer to use $_SERVER["HTTP_ORIGIN"], not always reliable/safe in my opinion (I had some browsers where HTTP-ORIGIN was always empty).
The normal way to do this in PHP with single front end domain is starting PHP code with:
header('Access-Control-Allow-Origin: https://one.example.com');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
header('Access-Control-Allow-Credentials: true');
And in JS on one.example.com domain:
jQuery.ajax({
url: myURL,
type: "POST",
xhrFields: {withCredentials: true},
dataType: "text",
contentType: "text/xml; charset=\"utf-8\"",
cache: false,
headers: "",
data: myCallJSONStr,
success: function(myResponse) {.....}
However, this is not workable as I am using multiple subdomains to call my API domain.
And this solution will NOT work as I want to pass on cookies:
header('Access-Control-Allow-Origin: *');
It conflicts with the pass on cookie setting on the JS site:
xhrFields: {withCredentials: true}
Here is what I did:
1 - use GET parameter to pass the Subdomain.
2 - Hardcode the Main domain in PHP so only (all) Subdomains are allowed.
This is the JS/JQuery AJAX part of my solution:
function getSubDomain(){
let mySubDomain = "";
let myDomain = window.location.host;
let myArrayParts = myDomain.split(".");
if (myArrayParts.length == 3){
mySubDomain = myArrayParts[0];
}
return mySubDomain;
}
And in the AJAX call:
let mySubDomain = getSubDomain();
if (mySubDomain != ""){
myURL += "?source=" + mySubDomain + "&end"; //use & instead of ? if URL already has GET parameters
}
jQuery.ajax({
url: myURL,
type: "POST",
xhrFields: {withCredentials: true},
dataType: "text",
contentType: "text/xml; charset=\"utf-8\"",
cache: false,
headers: "",
data: myCallJSONStr,
success: function(myResponse) {.....}
Finally, the PHP part:
<?php
$myDomain = "example.com";
$mySubdomain = "";
if (isset($_GET["source"])) {
$mySubdomain = $_GET["source"].".";
}
$myDomainAllowOrigin = "https://".$mySubdomain.$myDomain;
$myAllowOrigin = "Access-Control-Allow-Origin: ".$myDomainAllowOrigin;
//echo $myAllowOrigin;
header($myAllowOrigin);
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
header('Access-Control-Allow-Credentials: true');
IMPORTANT, don't forget to set the cookies for all subdomains, in this case the domain for the cookie would be: .example.com (so with a dot in front of the main domain):
<?php
//////////////// GLOBALS /////////////////////////////////
$gCookieDomain = ".example.com";
$gCookieValidForDays = 90;
//////////////// COOKIE FUNTIONS /////////////////////////////////
function setAPCookie($myCookieName, $myCookieValue, $myHttponly){
global $gCookieDomain;
global $gCookieValidForDays;
$myExpires = time()+60*60*24*$gCookieValidForDays;
setcookie($myCookieName, $myCookieValue, $myExpires, "/", $gCookieDomain, true, $myHttponly);
return $myExpires;
}
This solution allows me to call the API on api.example.com from any subdomains on example.com.
NB. for situation where there is only a single calling subdomain, I prefer using .htaccess for setting CORS instead of PHP. Here is an example of .htaccess (linux/apache) for only one.example.com calling api.example.com:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "https://one.example.com"
Header set Access-Control-Allow-Headers "Origin, Content-Type, X-Auth-Token"
Header set Access-Control-Allow-Credentials "true"
</IfModule>
And place this .htaccess in the root of api.example.com.

Facebook fbsr and session wont delete

I have a logout.php page. This gets called by clicking logout that has this javascript attached to it:
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
button.onclick = function() {
FB.logout(function(response) {
window.location = 'logout.php';
});
}
}
});
On the logout page i have this php code running:
if (isset($_COOKIE['fbsr_' . $app_id])) {
setcookie('fbsr_' . $app_id, $_COOKIE['fbsr_' . $app_id], time() - 3600, "/");
setcookie('PHPSESSID', $_COOKIE['PHPSESSID'], time() - 3600, "/");
unset($_COOKIE['fbsr_' . $app_id]);
unset($_COOKIE['PHPSESSID']);
}
The problem is that the javascript does log someone out. The php script will not remove the fbsr and the phpsessid cookies. How can I get around this issue?
For some of the applications, FB sets the fbsr cookie under ".your-domain.tld" domain (notice the preceding point). The cookie won't be deleted unless you specify the correct domain. Try this and you'll have logout working:
setcookie('fbsr_' . $appID, '', time()-3600, '/', '.'.$_SERVER['SERVER_NAME']);
It's a FB bug that makes the cookie not be deleted at logout, so your $fb->getUser() API call returns the former user ID instead of NULL or 0. This is FB world ;)
Greetings!
I had the same problem, even though all cookie pars were right (doublechecked)
What seems to work for me, is
setcookie($cookie_name, '', null, '/', '.'.$base_domain);
note the null value instead of: time()-3600
I honestly dont know why that works and the time()-3600 does not, but I cross checked and it works... HTH

Categories