I have a bit of a stumper that I am hoping someone has a fresh idea on.

The Problem:
I have a client that owns 3 domains all selling similar products. The domains are all very interlinked together for various SEO purposes. I need to be able to have a single cart (session data) exist on all 3 domains and display data regarding that cart on all three. The info to display basically includes a synopsis of their cart. I have full control over a VPS server environment from which all three domains reside. Furthermore all three domains server up the same index.php file from the same directory. The index file then parses the url string to deliver the proper content. The main advantage to this is that the session save path remains the same (big bonus I found). Because the session save path is accessible from all three domains all I really need to pass from one domain to another is the session id.

I have tried a number of solutions and each has fallen slightly short of what I need. Based on various searches around the web here is what I have tried.

Prepend each link on the site with the session id:
This solution is the most widely posted through the web it looks something like this

<? $sessionid=session_id();?>
<a href="http://example.com/page?sessionid=<? echo $sessionid;?>">Link</a>

then on the accepting page you use something like

<? $sessionid=$_GET['sessionid'];
session_is($sessionid);
session_start();
?>

This works but has a very open security issue as all someone has to do to hijack a session is put in a sessionid= at the end of any url on my site. Note I could use session_destroy() when the user is done with their visit but that is still not secure there is no clear way to determine when a user leaves permanently. No matter how you close the session it is still vulnerable.

Initiate a session across all domains at once
This solution uses a specified file on domain2 that is accessed by domain1 and again passes the session var. There are 2 variations I found on this.
Variation 1 : using cURL
from domain1 you curl a file on domain2 passing in the sessionid from domain1. domain2 takes in this session id and assigns it for the domain. the code looks something like

<? 
//page on domain 1 does this
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://domain2.com/index.php?sessionid=" . session_id());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$temp = curl_exec($ch);
curl_close($ch); 
unset ($temp);
?>

then on domain 2 we would have

<? $sessionid=$_GET['sessionid'];
session_is($sessionid);
session_start();
?>

So this worked in some browsers but not in others. firefox yes, explorer no
Variation 2 : using an image link
very similar to the first variation but in this case you use an image tag. this is less secure because upon viewing the code a hacker could see the method being used and again hijack the session. regardless the solution looks like this

<img src="domain1.com/session_setter.php?sessionid=<? echo session_id();?>" height="1" width="1" />

then the same script exists on domain2 as above

<? $sessionid=$_GET['sessionid'];
session_is($sessionid);
session_start();
?>

this works in firefox but not in IE

Maintain a DB of sessions
This is the best solution I have come up with but still has limitations. Basically the user is uniquely(sort of) identified in a db using their IP and User Agent. When they visit any page of the site their IP is cross referenced with the db to see if they have a session already started. If they do the session is re-implemented and everything works as it should. The code looks like this.

<?
  $result=mysql_query("SELECT sessionid FROM sessions WHERE ip='".$_SERVER['REMOTE_ADDR']."' AND time>'".(time()-(24*60*60))."' AND agent='".$_SERVER['HTTP_USER_AGENT']."' AND closed='0' ORDER BY time DESC LIMIT 1");
 if (mysql_num_rows($result)>0){
  $session=mysql_fetch_assoc($result);
  session_id($session['sessionid']); 
 }
 session_start();

        $time=time();
        if(isset($session['sessionid'])){
        mysql_query("UPDATE sessions SET time='$time' WHERE sessionid='".$session['sessionid']."'");
         }
        else{
        $agent=$_SERVER['HTTP_USER_AGENT'];
        $ip=$_SERVER['REMOTE_ADDR'];
        $sessionid=session_id();
        mysql_query("INSERT INTO sessions SET sessionid='$sessionid', ip='$ip',agent='$agent', time='$time'");
}
?>

Note: half of this is done at the top of the page the other half at the bottom.(doesn't make too big a difference but...)

OK so this work great except, 2 users on the same sub-network will have the same IP. If they happen to use the same type of machine and browser usera and userb will have the same session.

While this problem will not be run into regularly it still can happen. My main concern is the customer service department for this website. All the CS agents are likely on a similar computer managed by a single IT dept that updates everything at the same time. Thus everyone in that office will use the same IP and the same User agent.

If anyone has an idea or solution on this that I haven't tried please let me know. At this point I am open to just about anything. I think all I really need is a truly unique identifier for each users machine combined with the last solution would be perfect.

Recommended Answers

All 8 Replies

It seems that you could store the cart data in a cookie or in the database and just get it where and when you need it.

I have thought of this but I am not aware of a way to get cookies across different domains. In fact I am fairly sure a lot of effort has been put into preventing cross domain cookie access. This is a result of XSS security. Do you know of a way to access a domain1.com cookie from domain2.com?

Cookies cannot be used for security purposes like you mentioned. Those are out of the question.

Why can't these sites just be subdomains of the same domain? This would solve your problem.

The only way to really do this is with the url. This is really insecure, but there are some things that will beef up the security.

1. Regenerating the session id every page load. Look into session_regenerate_id(). This might be tricky with the multiple domains, but I can't see why it wouldn't work.

2. Checking the useragent for changes. Not completely reliable, but is a good way to detect attacks.

3. Still thinking if this one will work.

I would really prefer not to have to put the session id on every link. Partially for security but also because this site has a CMS attached to it and doing a regex search for links only to the local domains in order to add the sessionid to them could get somewhat cumbersome.

I did think of another option last night where I could use the third option and add in another column that determines whether or not the session id has been activated for a specific domain. Basically I could use this to limit each session id to only be activated on each domain once. There would still be potential for some mixup on this but I think it will solve the issue of users on the same network. I'll get back if the solution works. ...

So this is my current solution. Anyone see any vulnerabilities or potential issues?

<?
$result=mysql_query("SELECT * FROM sessions WHERE ip='".$_SERVER['REMOTE_ADDR']."' AND time>'".(time()-(24*60*60))."' AND agent='".$_SERVER['HTTP_USER_AGENT']."' AND closed='0' ORDER BY time DESC LIMIT 1");
	if (mysql_num_rows($result)>0){
		$session=mysql_fetch_assoc($result);
		if (strpos($session['domains'], $_SERVER['HTTP_HOST'])===false){
			session_id($session['sessionid']);
			mysql_query("UPDATE sessions SET time='".time()."', domains = CONCAT(domains, ' -- ".$_SERVER['HTTP_HOST']."')  WHERE sessionid='".$session['sessionid']."'");
		}
	}
	else{
		mysql_query("INSERT INTO sessions SET sessionid='".session_id()."', ip='".$_SERVER['REMOTE_ADDR']."', agent='".$_SERVER['HTTP_USER_AGENT']."', domains='".$_SERVER['HTTP_HOST']."', time='".time()."'");
	}
	session_start();
?>

I know from previous experience that some peoples ip address changes quite frequently so I wouldn't rely on that.

You might want to check to make sure that the user agent is actually sent. Some browsers will not send this which would cause errors. You cannot rely on this either.

By putting the HTTP_USER_AGENT directly into the query, you have opened yourself up to a sql injection vulnerability. That needs to be sanitized with mysql_real_escape_string().

I know the other solution is cumbersome, but it is the only way to do this in a way that will work for everyone.

A quick look at the code you posted, and I see a few issues.

1. ) You are using php short tags. While this is not "wrong" it is not enabled by default in most php configurations and become a real headache if you're not looking for it.

2.) None of the variables in your queries are filtered/validated in any way. Most of those headers you are using can be manipulated by the user in the request.

3.) You're using the mysql extension and I assume you're not using MySQL < 4.1.3. This is more of a pet peeve than anything but if interested read more: http://www.php.net/manual/en/mysqli.overview.php (halfway down or so)

As for some of the questions and thoughts you posed earlier.

Instead of passing the session id through the url, you could encrypt it with a key or a certificate that is unique to your sites. Then by base64 encoding (for url) the encrypted value, you could pass that value through the url as an identifier to your other sites.

You could use 3 1x1 images and use a url method just like you mentioned earlier to retrieve the encrypted value. Decrypt the value and set the session on those other sites to match. It is still a bit of a workaround, but it would stop you from passing the actual session id in the url. By using encryption it is a two way mechanism and you can decrypt it to retrieve the session value on the other sites. Check out the mcrypt to encrypt with a key/passoword and openSSL if you'd rather use a certificate.

OK, found my last solution didn't work right anyhow. I have modified taking into account sterilization of $_server vars. I did not switch to the mysqli format, though i will read on this and try to learn the new format for the future.

here is the now working solution. This seems to work very well. There will never be an issue between users that have different ips. There shouldn't be an issue with a user that switches ips mid session unless they are switching with each link click (if that is the case they have bigger issues and are likely doing something they shouldn't.) Furthermore visitors within a single network will never end up sharing a single session even if they have the same IP and Agent.

is there a situation where this would not work?

<?php	$result=mysql_query("SELECT * FROM sessions WHERE 
							ip='".mysql_real_escape_string($_SERVER['REMOTE_ADDR'])."' AND 
							time>'".(time()-(24*60*60))."' AND 
							agent='".mysql_real_escape_string($_SERVER['HTTP_USER_AGENT'])."' AND 
							closed='0'
						ORDER BY time DESC LIMIT 1") or die (mysql_error());
	if (mysql_num_rows($result)>0){
//		$message.="this visitor has been here before...";
		$session=mysql_fetch_assoc($result);
		if (strpos($session['domains'], mysql_real_escape_string($_SERVER['HTTP_HOST']))===false){
//			$message.=" Their session has not been activated for this domain so we will start it with the same session id";	
			session_id($session['sessionid']);
			mysql_query("UPDATE sessions SET time='".time()."', domains = CONCAT(domains, ' -- ".mysql_real_escape_string($_SERVER['HTTP_HOST'])."')  WHERE sessionid='".$session['sessionid']."'");
			session_start();
		}
		else{
//			$message.= " They have had a session set for this url before";
			session_start();
			if (session_id()!=$session['sessionid']){
//				$message.= " However this is a new visit for the user so we will start a new session";
				mysql_query("INSERT INTO sessions SET 
												sessionid='".session_id()."', 
												ip='".mysql_real_escape_string($_SERVER['REMOTE_ADDR'])."', 
												agent='".mysql_real_escape_string($_SERVER['HTTP_USER_AGENT'])."', 
												domains='".mysql_real_escape_string($_SERVER['HTTP_HOST'])."', 
												time='".time()."'");
			}
		}
	}
	//adds new session
	else{
//			$message=" this is a new user and a session was not found so a new one was started";
			session_start();
			mysql_query("INSERT INTO sessions SET 
												sessionid='".session_id()."', 
												ip='".mysql_real_escape_string($_SERVER['REMOTE_ADDR'])."', 
												agent='".mysql_real_escape_string($_SERVER['HTTP_USER_AGENT'])."', 
												domains='".mysql_real_escape_string($_SERVER['HTTP_HOST'])."', 
												time='".time()."'");
			
	}
//	echo $message;
?>

still need to clean my debugging comments...

If this still seems insecure to any of you I will look into the encryption method a little deeper.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.