Secure and Flexible Cross-Domain Sessions

I am having an issue that I hope you can help with. Let's say I work for a hypothetical company called "Blammo", and we have a hypothetical product called "Log". I am trying to set up a system where someone could log in to logfromblammo.com and order some of our products, and then when they are ready to purchase go to checkout.blammo.com to pay for their order. Eventually I want to allow for Blammo to launch a new hypothetical product with it's own website: rockfromblammo.com, and have that site also able to share a session with checkout.blammo.com so that users can have a single shopping cart across both product websites.

Naturally the hypothetical scenario described above is not how my company actually works, but it is a fair example of what I need to do. We have an existing user database, and we have ways to authenticate any of our users on any of our sites, but the goal I have is to allow users to cross seamlessly from one site to another without having to re-authenticate. This would also allow for us to seamlessly transfer data such as a shopping cart to the checkout site.

I have (briefly) looked at solutions such as OpenID, but I need to be able to integrate whatever solution we have with our existing authentication method, which is not terribly robust. Is there any good way to do this through PHP alone?


Solution 1:

What you could do is create "cross-over" links between the sites to carry the session over.

The simplest way is to pass the session id via the query string; e.g.

http://whateverblammo.com/?sessid=XXYYZZ

Before you start thinking that anyone can trap that information, think about how your cookies are transferred; assuming you're not using SSL, there's not much difference for someone who taps the network.

That doesn't mean it's safe; for one, users could accidentally copy/paste the address bar and thus leaking out their session. To limit this exposure, you could immediately redirect to a page without the session id after receiving it.

Note that using mcrypt() on the session id won't help much, because it's not the visibility of the value that's the problem; session hijacking doesn't care about the underlying value, only its reproducibility of the url.

You have to make sure the id can be used only once; this can be done by creating a session variable that keeps track of the use count:

$_SESSION['extids'] = array();

$ext = md5(uniqid(mt_rand(), true)); // just a semi random diddy
$_SESSION['extids'][$ext] = 1;

$link = 'http://othersite/?' . http_build_query('sessid' => session_id() . '-' . $ext);

When received:

list($sid, $ext) = explode('-', $_GET['sessid']);
session_id($sid);
session_start();
if (isset($_SESSION['extids'][$ext])) {
    // okay, make sure it can't be used again
    unset($_SESSION['extids'][$ext]);
}

You need these links every time a boundary is crossed, because the session may have gotten regenerated since the last time.

Solution 2:

It can be done, but not with simple cookies and it is not trivial. What you are after is a single sign on (SSO) solution, similar to Google's which share's login's across i.google.com, gmail.com, youtube.com etc.

I have used OpenID to implement this in the past.

The basic idea is to have a single authentication domain (Provider), whenever one of the sites (Consumer) wants to authenticate the user, they redirect them to the authentication domain. If they aren't signed in, they can log in using whatever details you require.

If they are already logged in (even from a different target site), they don't need to log in again.

The user is then sent back to the target site with the addition of a token in the url. This token is used by the target site's server to verify the user is authenticated with the authentication server.

This is an extremely simple explanation. Doing this is not difficult, doing it securely is much more so. The details of generating and authenticating the tokens securely is the challenging part. Which is why I suggest building upon a well designed system such as OpenID.

Solution 3:

In cross domain Ajax, you may find that cookie and, followingly, session are lost for cross domain requests. In case you'll be making ajax calls from you site example.com to your subdomain s2.example.com you will need to use properties in headers for PHP:

header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Credentials: true');

and in JS you going to add

xhrFields: { withCredentials: true }

Otherwise cookies are not passed and you can't make use of session on your subdomain.

Full JS request to subdomain will be without lost of session:

$.ajax({
    url: "https://s2.example.com/api.php?foo=1&bar=2",
    xhrFields: { withCredentials: true },
    success:function(e){
        jsn=$.parseJSON(e);
        if(jsn.status=="success") { 
                alert('OK!');
        } else {
                alert('Error!');
        }
    }
}) 

Solution 4:

You need to set the session cookie domain like so:

session_set_cookie_params($lifetime,$path,'.site.com')

This will only work if the sites are on the same domain name including TLD (Top Level Domain).

See here for more info

Alternatively, if you are looking at trying to access sessions cross domains, as in from site1.net to site2.com, then this cannot be done.