Cross domain cookies - php

I have a small problem.
How do I set a cookie for multiple domains?
I do understand the security problems, and I am sure it has been done before. The reason for this is SSO.
ie.
account.domain.com will need to set domain logged in for:
domain.com,
domain1.com,
domain2.com.
Is there any easy way, using PHP and cookies, or any alternatives?

There is absolutely no way for domain.com to set a cookie for domain1.com. What you are attempting to do can only be solved by getting the user's browser to submit requests to each domain which will then set its own cookie.
Then you need a way for each domain to verify the user's identity. There are two approaches to this:
Back channel - the sites contact each other directly to determine if a user is logged in.
Passing a token in the GET or POST - when the user's broweser is redirected to the other site a digitally signed parameter is passed containing the identity and session status.
It's really quite complicated. I suggest you don't roll your own. Take a look at SimpleSAMLPHP for a PHP implementation of what I'm describing.

What you're attempting can't be done. (It's a browser security issue, not a PHP one.)
Other than using some form of off-site authentication, the nearest you can achieve is making a cookie accessible across sub-domains, in which case you just use the optional 'domain' arg of PHP's set_cookie function.

This can be done via one domain acting like a master and others like a slave.
Say we've got a domain accounts.domain.com and it's our master.
Then we've got our slaves domain.com, something.com and another.com
When you'll log on on domain.com, it'll be actually site accounts.domain.com, then you'll get a cookie with unique ID for your browser and then you'll be redirected to domain.com's post-logon landing page (ie. domain.com/logon?check=true&unique-id=<browser unique id>&request-id=<unique request ID>). the landing page will contact the accounts.domain.com, querying it with the browser ID. If the transaction's okay, then you'll get logon cookie from domain.com.
Next, on every domain (domain.com, something.com and another.com) will be initial redirect to accounts.domain.com/roaming-check?return-url=<URL the redirect was initiated from>. Because we're returning home (we're logged already on accounts.domain.com), we'll be redirected again on our landing page (<domain name>.com/logon?check=true&unique-id=<browser unique id>&request-id=<unique request ID>) and from this point it's the same as the part with logging on. We're seamlessly roamed to another domain (without user knowing it as browsers doesn't usually show the redirected page until it passed the headers send(server)/receive(browser) section).
In case there's in fact no active logon, the site will save this "negative logon" to session and not try to check logon anymore (until we try to logon or load another domain).

I think this solution will suit your needs: "Simple Single Sign-On for PHP"

Related

Cross Domain Sign In

I have a few domains all on the same server, with the same IP and the same databases - that can be accessed by all 5 of the domains.
I have recently remade my login system, so that on my main domain, the cookie works for not only the main domain but the sub domains as well. What this means is that if a user logs into one area, they are signed in everywhere. Which is great! I write a cookie with their hash (taken from the DB) and check for that when loading each page, and they are automatically securely signed in.
This is lovely, but the problem then comes when switching domains, as cookies seem to be locked down to domains. So my other domain (lets call it domain2.com) cannot read the cookie from domain1.com.
Are there any clever ways around this? I could write something to the database, such as IP, but that wouldnt be very secure as the company i work for everyone is on the same IP and therefore it wouldnt be specific.
Or I thought about maybe including a hidden iframe on the page, which actually links to a page on the main server, and pulls the information that way somehow.
I am not sure, but I am sure it can be done. Any ideas?
Browsers, for good reasons, do not allow cookies to be read from any other domain.
What you can do is have domain2.com redirect to a page on domain1.com which checks if the user is logged in and if they are it redirects back to domain2.com with the user's id which can then log them in.
You should not depending on original PHP session functions Collections.
Here is what I have done :
After login success , Server side should return a "session ID" to the browser and store by JavaScript or some how, mean while the "session ID" should be store in database as a successful signal and you do a login time next to the session ID if you needed.
Now you can share the session ID in any IP server you want and make your client connect to(some trick like you redirect to the new domain and post the SID) then establish a PHP session.

Cookie browser behaviour on redirects from a domain to another

Suppose that amazon.com sets a redirect header on its homepage to google.com. Upon visiting amazon.com, the browser fetchs the response, reads from the header that it should head over to google.com, and proceeds to make another request to google.com. I understand that in this second request, the browsers sends the cookies that the user may previously had before with google.com, is this correct? That is to say, if the user was previously logged in to his account on google.com he will appear logged in when amazon.com redirects there.
Just trying to make sure I understand all corner cases in interaction with external parties in a web app.
"The browsers sends the cookies that the user may previously had before with google.com, is this correct?"
Yes.
Unless the cookie has expired, whenever a domain/path is hit that has a cookie stored for it, that cookie is automatically sent with the request headers.
https://www.rfc-editor.org/rfc/rfc6265#section-4.2
However Google's login procedures are fairly complex as it covers all their domains and systems. It may redirect around a little to reestablish the users login via a "remember-me token" and a fresh cookie being set from their central auth domain. They aren't the best straight-forward example but your general understanding of it is correct.
The definitive guide to form-based website authentication
This community wiki includes some good details on different authentication techniques, including "remember-me tokens".

Authenticating across different subdomains (but not all)

I have a site set up on www.domain.com, the site can authenticate users and persist their credentials in a cookie.
On occasions the users access handlers that are set up on different servers on a different sub domain. handlers.domain.com
I can't afford to use wildcard subdomain cookies (Cookies should not be available for other subdomains)
My solution for access control up until now was that every URL used for handlers.domain.com had a guid specific to the user. The handlers on the other site would assume the identity of the guid owner. This of course is not such a good security practice.
i was thinking about an alternative solution: All links to handlers.domain.com will actually be links to a redirector script on www.domain.com that will redirect to an encrypted time stamped url on handlers.domain.com which will then know for sure that it was accessed as a direct authenticated redirection from www.domain.com.
This solution will work fine on GET scenarios but will fail with handlers expecting POST data (up do big uploaded files)
Does anyone know or can think of a better solution or have any insight on my solution?
(In this case I am using ASP.NET but the solution will probably be platform agnostic, so I will tag this with various web platforms)
Thanks!
As you do not want to use cookies to establish a session (group of requests) you need to find other ways. As the information of the cookie is passed readable within the HTTP request, I do not see a problem if you for that one particular request pass that information as part of a POST request.
If you prefer a GET request I would additionally add a flag inside the users server-side session prior the redirect so to give the script that is the destination of the redirect the possibility to verify the validity of the request on the server-side.
You said you "can't afford to use wildcard subdomain cookies (Cookies should not be available for other subdomains)". Does that mean you can't afford it monetarily or you you don't want the user to have access to all subdomains? If it's the second, you could still use subdomain cookies by putting in an encrypted value with that user's ID and check it versus access permissions on your various subdomains. That keeps everything at the server where it's more secure versus at the URL level. The only way a potential hacker can get past it is to guess another user's ID and figure out your keys for properly encrypting it.

Cross domains sessions - shared shopping cart cross domains

we are solving the problem with eshop (php, mysql). The client want to have the same eshop on two domains with shared shopping cart. In the shop customer can do the shopping without users account (can't be logged in). And there is the problem, how to make the shared shopping cart cross domain.
The data from cart is stored in sessions, which we stored in database too. But we can't solve the problem in carrying data over domains. Identifying unlogged user is not holeproof (research).
The example, how it should work
Customer goes to domainOne and add some things to the cart. Than he goes to domainTwo (by link, typing domain address, however) and add some other things to the cart. In the cart he has things from both domains (after refreshing page).
Do you have any idea, how to solve this problem?
What didn't work:
redirecting is not possible due to customer requirments
cookies are related to domain
set_cookie with the other domain didn't work
the simpliest way is to carry over only the sessionid (stored in cookies) but we don't know, how to wholeproof identify unlogged users.
is there any other place, where data can be stored on client side except cookies? (probably not)
we can't use sending sessionid by params in url (if user click to link to the other domain) or resolving the header referer, bcs we don't know, how user can achieve the other domain.
If you can't understand me, take me a question. If you think, that having eshop on two domains with shared (common) cart is bad idea, don't tell me, we know it.
Thanks for each answer.
You can use a third domain to identify your customers over all domains.
Use for example a PHP File on http://thirdDomain.com/session.php that is included on all pages on both shops.
Sample:
<script type="text/javascript" src="http://thirdDomain.com/session.php"></script>
After your customer switches domains, you can identify him as the same customer using the third domain.
You can assign the session id on both shops to the session id on the third domain to access the cart on both shops. You only need to inform the third domain about your shop sessions (i.e. add them as parameter).
Depending on how flexible you are with your code and templates, you can even use an output from the third domain to define the session id in your shops. This way you can use the same session id on all domains.
But normally a session id assignment should be the more secure way.
Using the javascript version you can also output scripts that may add a session id to all outgoing links and forms to the other domain in the current html page. This might be interesting if you can identify your customer as having cookies blocked.
You can also use the javascript to inform the parent document about an existing session.
This keeps getting asked.
Have a search for SSO.
You need to pass the session id in the URL (or vai a POST) across the domains, then:
1) check the session does not already exist on the target domain
2) rebind the session using the session id sent
e.g.
if ((!$_COOKIE[session_name()]) && $_GET['passed_id']) {
if (check_session_exists($_GET['passed_id'])) {
session_id($_GET['passed_id']);
}
}
session_start();
...
function check_session_exists($id)
{
$path=session_save_path() . $id;
if (file_exists($path) && (time()-filemtime($path)<session_cache_expire())) {
return true;
}
return false;
}
This also means you need to add '?passed_id=' . urlencode(session_id()) to any URL pointing to the other domain.
C.
The schema is quite simple and widely used. By google for it's numerous services for example. You have a whole picture by tracking down HTTP interchange between your browser and various google services to get the idea.
Suppose we have our client authorized for the 1st domain. By getting to the second, we have to:
start a session and store some token in it.
ask browser to request 1st domain somehow and send this token along.
1st domain will recognize our client and make a connection in the shared database between this token and user id.
By requesting second domain again, we will have it authorized for it's already started session.
The only question remains is how to request 1st domain. It can be a picture, or JS request or entire page redirect. Certain choice is up to you.
You can use Flash LSO's for this matter i think. Normally LSO's are stored in their domain specific sandboxes, but if two domain objects allow, they can communicate as stated in the "cross-movie communication" section in http://download.macromedia.com/pub/flash/whitepapers/security.pdf.
For general info about LSO's:
http://www.adobe.com/products/flashplayer/articles/lso/
SSO.
CartA has iframe that 1) checks if the user is "active" (has session) 2) creates anon session
CartB has iframe that do 1) or 2)
iframe loads from SSO domain (any domain you can have)
SSO solution: build yours or use others - like simplesamlphp or something...
And there should be no need to pass sessions/params with URIs...
You can store data in other places than cookies (e.g. Flash cookies, localStorage) but all use same origin policy, which is the standard security model of the web: data stored by a domain can only be accessed by that domain and its subdomains. The standard workaround is to embed an iframe from the foreign domain into the page. That iframe will have access to the cookies of the foreign domain, and its url will be controlled by the local domain, which allows for communication.
A simple solution based on that is to have a table of (domainA sessionid, domainB sessionid) pairs. When a new user arrives to domainA, (new sessionid, NULL) is added to the table; the page shown to him includes an invisible iframe with source = http://domainB/mergeSessions.php?sessionA=1234. mergeSessions.php will then receive sessionA as an URL parameter and sessionB as a cookie, and update the session link table accordingly.
You could attempt to identify your visitors by IP, browser type, browser version, OS, screen resolution, and whatever else you come up with. That you store in the shared database when someone accesses either site.
If, within a small time window, say < 5 min, requests from that IP with those parameters comes, you can reasonably assume that it's the same user. Again, make sure you use everything you can find find to identify that user and by no means base anything secure on this or you will be subject to hijacking.
What about something like this, not sure how good it would be though.
User goes to store1. If user does not have a session cookie, redirect to a special page on store2 asking for the session id and sending the url on store1 to return to. The special page looks at the session cookie and redirects back to the original url on store1 with the session id (like the answer by #symcbean). Then on store1, the session cookie gets set(or created new) and no more redirecting happens. And then the same but oposite if the user is on store2 with no session cookie.
But if the user does not have cookies enabled, I can see an infinite loop happening. Not sure if it would be possible to detect and stop somehow.
But this way would be hacky at best.
1) Obviously, use the same session-store for both domains (files, database, memcached, the usual suspects.
2) If after session_start() the $_SESSION is empty, create an 'all domains' array in the session (do this on every domain, regardless which one it is, ).
$_SESSION['all_domains'] = array(
'domain1.com' => true, //<= current domain the customer is on,
'domain2.com' => false, //other domain, no cookie for it yet.
'domain2.com' => false); //repeat for all domains needed
3) Create a session-setter script on all domains (let's call it 'sesset.php':
<?php
if(isset($_GET['sessid']){
session_id($_GET['sessid']);
session_start();
//also, check here for the domains:
if(!isset($_SESSION['all_domains'])){
//set the array as before, flag this domain as true.
} else {
$_SESSION['all_domains'][$_SERVER['HTTP_HOST']] = true;
//you might want to set a custom domainname instead of HTTP_HOST, so you won't get doubles from domain with & without www. and so on.
}
}
?>
4) On every conceivable php HTML page, put this somewhere near the end of the body:
<?php
foreach($_SESSION['all_domains'] as $domain => $domainset){
if(!$domainset){
echo '<img src="http://'.$domain.'/sesset.php?sessid='.session_id().' width="1" height="1"/>';
}
}
?>
Not fullproof, but will get almost all users. Ofcourse, one could do it with a redirect cascade instead of 'hidden images', but searchbots (google et al.) very much get confused about it, especially if they don't remember the cookie and are stuck being redirected again & again.
easyXDM is a framework that allows the user to easily work around the Same Origin Policy.
Its built-in RPC feature is very easy to use, and you should be up in running in no-time.
For your case, select one of the domains to be the 'checkout'-domain (A) - this is the domain that will keep the session stored. On the same domain you create a small file with an easyXDM endpoint that is responsible for storing/retrieving the data sent from the other domain (B).
Now, in domain B, you include easyXDM and when storing/retrieving data from the cart, you access the RPC methods instead.
Option 1 Use Iframes:
Site 1 has an Iframe of site 2
Site 2 has an Iframe of site 1
When a user selects an item from site one, set the iframe value to a dynamic string ie domain2.com/iframe.php?itemid=someitem.
Have domain2 grab the $_GET information with PHP from the iframe and update the user's cookie.
Do the same in the other direction.
Option 2: Javascript includes
You can do something similar with cross-site included JS files generated by PHP to "pull" the contents of the user's cookie to the other site.
Option 3: Curl
Just post the data from one domain to the other, so both have a copy. This is the least secure method since there is no guarantee that the IP address or other identifying data can't be duplicated. Though, you can have some "question" or pass phrase to ensure it is the same person. Possibly by setting an email address?
Option 4: Third-party cookies
I think this one was already mentioned, but you can set the cookies from a third domain, so both sites functionally exactly the same rather than "toggling" back and forth between the two.

Is it possible to set a cookie for a site /server other than you own?

Here's the quick version of my question:
Is it possible to set a cookie somehow into a client's browser when the cookie is for use with a different server (in this case an Exchange mail server)? In this scenario the server trying to set the cookie is at "intranet.myschool.edu" and the exchange server is at "owa_server.myschool.edu".
Here's the full question:
I have a php script that uses cURL to make an HTTP POST to our Exchange server that has Forms Based Authentication enabled.
When I make a successful HTTP POST (which includes the user/pass in the posted url), the Exchange Server (or more specifically, the https://my.school.edu/exchweb/bin/auth/owaauth.dll file) outputs cookies.
Specifically, it outputs a "sessionid" and a "cadata" id.
With these cookie ids written to a text file on the server, cURL/PHP can reference it and then request data (via webdav and such) from the Exchange/OWA server.
That part works.
The problem I'd like to solve is now passing the cookie ids to a clients browser, so that they can use these cookie ids to auto-login to their own OWA account.
In essence I would like our users to log into our intranet with their Active Directory IDs, and see a snapshot of their recent emails. Then, if they need to, I'd give them a little link to switch over to the full OWA web application. When this switch happens, I don't want them to have to login to the OWA manually. Since they already submitted their Active Directory UserName and password at the front of the intranet, I'd like them to be auto-logged into the OWA.
I should note that using Windows Authentication to try to do single sign on is not possible since we have a mix of Mac OS, Windows, and Linux.
I had thought that I would be able to do a "setcookie" and assign the cookie ids that cURL got and put them into the clients browser.
Is this not possible? Is it not possible to "spoof" Exchange/OWA (or any other site) this way.
I have legitimate cookie ids that cURL captured. Is there no way to pass these to a client browser on a different computer?
In a worst case scenario, would using Javascript to just auto paste the username and password into the OWA login page be my only hope?
Does anyone have any other ideas on how to avoid my double login problem with Exchange/OWA?
Thanks for any help provided!
From RFC 2965 (NB HDN = "host domain name)
Host A's name domain-matches host B's
if
* their host name strings string-compare equal; or
* A is a HDN string and has the form NB, where N is a non-empty
name string, B has the form .B', and B' is a HDN string. (So,
x.y.com domain-matches .Y.com but not Y.com.)
Note that domain-match is not a
commutative operation: a.b.c.com
domain-matches .c.com, but not the
reverse.
So using .myschool.edu as the domain should work. NB the leading . is essential
You may be able to set a cookie with a domain part of '.myschool.edu'. In theory that's then sent to any other site hosted under a subdomain of 'myschool.edu'.
In practise however, your client software may decide that the cookie's scope is too wide, and refuse to send it back.
I think this would be a serious security loophole if it were possible...
In this scenario the server trying to set the cookie is at "intranet.myschool.edu" and the exchange server is at "owa_server.myschool.edu".
You should be able to do that.
I do this on my site (which I will change the names for the purpose of the example):
I have a web app at url
webapp.domain.com
And when users login, I set the cookie of the PunBB forum package which is at:
forum.domain.com
By setting/clearing the PunBB forum cookie, I can automatically login/logout my users on their forum account for convenience (this of course assumes that the registrations are syncrhonized, in my case I removed the forum registration and the main site registration creates the forum account for the user).
All you need to do is in subdomain#1 to set the cookie path to "/" (the default), and set the cookie domain to "domain.com". Then your app in subdomain#2 should see the cookie.
EDIT: I see barrowc has answered, I've seen the ".domain.com" pattern in some examples, my site uses "domain.com" for the cookie domain and it works too (maybe php set_cookie adds the leading dot if missing?)
Your browser gets to decide that... but usually no, you cannot. That is considered a type of XSS vulnerability.
you could use an iframe to set the cookie, ie. have an iframe on your web server that makes a request to a page on your exchange http server (https://my.school.edu/exchweb/) with your wanted cookie vars set as get or post variables. then use the vars to set the cookie for that domain, and redirect the user to the exchange server.
now, there could be logic on the backend of OWA that checks ip address, user agent, etc.... when registering the session that may invalidate this..... not sure
We've been fighting this one hard for months, the best we can come up with is allowing the web server to get the cookie for Exchange at EVERY LOGIN. problem is, that without cookie affinity, we don't have a way to make sure that the cookie obtained by the web server came from the same load balanced node that the client connects to.

Categories