PHP sessions across 2 subdomains

This is an addition to PHP sessions on subdomains
I tried what is stated in this question and I see that the problem was not given.

So, I need to have sessions between subdomains ( www.example.com

before forum.example.com

)

What I did on www.example.com

,

session_name("a_name");
session_set_cookie_params(0, '/', '.example.com');
session_start();

echo session_id();
$_SESSION['test'] = 123;

      

On forum.example.com

session_name("a_name");
session_set_cookie_params(0, '/', '.example.com');
session_start();

echo session_id();
print_r($_SESSION);

      

Session_id is exactly the same, but $ _SESSION doesn't output anything.
How to make a conclusion forum.example.com

123

?

I tried session.cookie_domain = .example.com

but didn't change anything

When I go to forum.example.com

it destroys sessions www.example.com

and it does the same on the other side, for example if it detects that it comes from a different subdomain and wipes everything out for security.

2 subdomains are on the same Debian server

Another thing I noticed is that without session_name

and session_set_cookie_params

it still has exactly the same session_id when I setsession.cookie_domain

thank

+3


source to share


2 answers


Ok, I thought about this for a while and I think I got it.

First of all: since you get the same session ID from the servers , we can rule out any cookie-related problems. Obviously you successfully create a cookie named a_name

(although I only recommend alphanumeric characters for that cookie name ) on www.example.com

and successfully read that a_name

cookie on forum.example.com

. But as you said, you are not getting any data from forum.example.com

. session.cookie_lifetime = 0

no problem: it means that the cookie remains until the browser is closed .

We need to delve a little further into PHP session handling. The session id you read with session_id()

refers to a file on your server. Typically, this file is present in the /tmp/sess_$session_id

. The content of this file is a $_SESSION

serialized array . (Keep in mind that the data is not serialized in the same way that serialize()

PHP does ... but that doesn't matter right now.)

I think this is a file permissions issue:

  • /tmp/sess_$session_id

    the file is installed using user and group www.example.com

    .
  • forum.example.com

    tries to open /tmp/sess_$session_id

    but does not have the appropriate permissions .
  • As a result, you get an empty result when you try print_r($_SESSION);

Solution :
Check the server config file to make sure www.example.com

both forum.example.com

are working as USER AND GROUP ONLY . It is very important! For Apache, find the * .conf file:



User youruser
Group yourgroup

      

For nginx, find nginx.conf:

user youruser yourgroup;

      

If changing the server configuration files is not an option, you must ensure that the users working on the two sites are in the same group.

You can verify that this is the problem by first downloading www.example.com

and then sudo ls -ltc sess_*

into your server shell via SSH (look for the sess_

one ending in $session_id

). Then download forum.example.com

and then sudo ls -ltc sess_*

again to see the user and / or group change.

+2


source


For this answer, I made several assumptions:

  • User must enter credentials at least once in each domain (any other method would be a serious security issue).
  • You have access to a database or file space outside of your web root.
  • subdomain, domain or any other name will be called "site"
  • The goal is to have a common file (physical file or serialized in a database) accessible from every site / domain.

In my example, I will be using a database, since this is the idea I am using, not the database / file access methods, I will have unnecessary rows deleted. IE: How to connect to the database.


If this concept is what you were, or if anyone else wants me to fill in the blanks for completeness, just leave a comment. With code.




I would take a completely different approach.
From what I am gathering from your question and the associated post you linked to, you are trying to share a session using the common session name.

  • Each site has its own session ID.
  • Each site has its own cookie authenticator ($ _COOKIE ['userid'] or $ _COOKIE ['userhash']).
  • Separate sessions are created and a shared cookie is stored on each site.
    • Using its own session handler, each site reads the same data. class MySessionHandler implements SessionHandlerInterface

    • My after thought was an even simpler approach, a class that acts as a session handler, read / write to a shared file. As the php session handler does not save data until the script ends.

Original idea - won't go into details, this is just for reference.

class MySessionHandler implements SessionHandlerInterface {
    private $savePath;

    public function read($id) {
        $id   = some_user_authentication_function();
        $hash = $_COOKIE['_h'];

        $result = mysql_query("SELECT sess_data FROM login_table WHERE user_id = {$id} AND hash = {$hash}");
        return $result['sess_data'];
    }

    public function write($id, $data) {
        $id   = some_user_authentication_function();
        $hash = $_COOKIE['_h'];

        $result = mysql_query("UPDATE login_table SET sess_data = {$data} WHERE user_id = {$id} AND hash = {$hash}");

        return ($result === false) ? false : true;
    }
}

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();

      





class customSessionHandler
{
    private $hash;
    private $id;
    private $sess_db;

    public function __construct($db) {
        $this->hash    = $_COOKIE['hash'];
        $this->id      = some_user_authentication_function($this->hash);
        $this->sess_db = $db;
    }

    public function get($key) {
        $query = 
            "SELECT value ".
            "FROM ".$this->sess_db.
            "WHERE user_id = {$id} ".
            "    AND hash = {$hash} ".
            "    AND key = {$key}";

        $result = mysql_query($query);
        return $result['key'];
    }

    public function set($key, $val) {
        $query = 
            "REPLACE INTO ".$this->sess_db.
            "SET {$key} = {$val} ".
            "WHERE user_id = {$id} ".
            "    AND hash = {$hash}";

        return (mysql_query($query) === false) ? false : true;
    }
}
$handler = new customSessionHandler('sess_data');
session_start();

      




As stated at the beginning, any code that is not essential to explain the concept has been removed.
Things that may not be obvious to everyone: - $ key and $ val need to be sanitized before being sent to the database. (prevent injection) - The hash is sent to your login functions, so the hash can be used to clean up the session data when needed, can also be used when authenticating the user. - Ready-made mysql statements would be ideal here, so you can prepare two queries in the constructor and then just reuse the statement for each call. Then put the code for closing the connection in the destructor.




After the thought


It would be a lot more secure if every site had its own hash.
Then, if you find a security anomaly, you can simply block or re-request credentials from one site without compromising the hash for the site network.


To implement this would be as simple as creating another table containing: - User ID - site_name (example.com) - Hash - Timeout - Re-authenticate

and by modifying the session_data table, so instead of accessing the $ key => $ val pair with a hash, you access it user_id.




Thanks for reading, I hope it will be helpful to someone.

+1


source







All Articles