3

Edit 29/1/2019 - this question was eligible for a bounty but none of the provided answers directly address the issue. If you have an answer that works based on what's been asked please respond

I have 2 domains configured on the same physical server:

app.example.com
help.app.example.com

Users login to https://app.example.com/ which consists of PHP 5.5 application which stores some data in the $_SESSION array after a successful login.

I want to configure https://help.app.example.com/ so that I can read the session data present on https://app.example.com/. The application on the help subdomain is a content management system built in PHP 7.

In Plesk I have added the following to 'Additional configuration directives' for php.ini under both app.example.com and help.app.example.com:

session.cookie_domain = ".example.com"

If I upload a phpinfo() script to help.app.example.com it is showing the following for session.cookie_domain:

  • Local value: no value
  • Master value: .example.com

If I then run the following in a script on help.app.example.com:

<?php
session_start();
var_dump($_SESSION);
die;
?>

It is outputting an empty array:

array(0) { }

However, if I run the equivalent on app.example.com it is outputting an array of session data which shows details of the logged-in user (as expected):

array(15) {
   ["o_id"]=> (1) "1"
   ["u_id"]=> string(4) "1745"
   ...
}

I'm expecting to see the same output on both sub-domains. Why is this not working?

I have read Allow php sessions to carry over to subdomains but none of that resolves the problem.

Andy
  • 5,142
  • 11
  • 58
  • 131

2 Answers2

3

Why is this not working?

by default sessions are saved to local files on the server, location of which is specified in php.ini's session.save_path, for example session.save_path = /var/lib/php/sessions, if app.example.com and help.app.example.com are running on 2 different servers with their own filesystem, or even if it's running on the same filesystem but have different session.save_path directives in php.ini, they won't share the same $_SESSION.

if you want 2 different servers to share the same $_SESSION, possible solutions include creating a shared session store database with session_set_save_handler() (like MongoDB or MySQL comes to mind), or creating a networked filesystem and set session.save_path = /path/to/networked/filesystem/mountpoint in php.ini, but both of these methods may incur a significant performance penalty..

... since the cookie is shared across both domains, session_id() will return the same value on both sides, that could be used as an id for a session database, take a look at http://php.net/manual/en/class.sessionhandlerinterface.php

(i'd write a sample class if i had more time but i'm out of time)

switch to a sql-db-backed session store (like MariaDB, MySQL, or PostgreSQL), for example: schema:

CREATE TABLE sessions (
  id VARCHAR(255) ,
  atime BIGINT ,
  data BLOB
)

SessionHandlerInterface implementation:

class MySqlSessionHandler implements SessionHandlerInterface
{
    protected $db;
    public function __construct(string $dsn, string $username, string $password)
    {
        $this->db = new PDO($dsn, $username, $password, array(
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ));
    }
    protected function a(string $id) : bool
    {
        $ret = $this->db->query("UPDATE sessions SET atime = " . (time()) . " WHERE id = " . $this->db->quote($id));
        return ($ret->rowCount() > 0);
    }
    public function close() : bool
    {
        // TODO: implement locking/race-condition-free session handling?
        return true;
    }
    public function destroy(string $id) : bool
    {
        $this->db->query("DELETE FROM sessions WHERE id = " . $db->quote($id));
        return true;
    }
    public function gc(int $maxlifetime) : int
    {
        $this->db->query("DELETE FROM sessions WHERE atime < " . (time() - $maxlifetime));
        return 1; // ??? not sure what this return int is supposed to contain, docs doesn't say either
    }
    public function open(string $save_path, string $session_name) : bool
    {
        if (!$this->a($session_name)) {
            $stm = $this->db->prepare("INSERT INTO sessions (id,atime,data) VALUES(?,?,?);");
            $stm->execute(array($session_name, time(), serialize(null)));
        }
        return true;
    }
    public function read(string $session_id) : string
    {
        if (!$this->a($session_id)) {
            throw new \InvalidArgumentException("supplied session id does not exist.");
        }
        return $this->db->query("SELECT data FROM sessions WHERE id = " . $this->db->quote($session_id))->fetch(PDO::FETCH_ASSOC)['data'];
    }
    public function write(string $session_id, string $session_data) : bool
    {
        // optimization note: this function can be optimized to do everything in a single query, instead of using a() (which also use a query)
        if (!$this->a($session_id)) {
            throw new \InvalidArgumentException("supplied session id does not exist.");
        }
        $stm = $this->db->prepare("UPDATE sessions SET data = ? WHERE id = ?");
        $stm->execute(array($session_data, $session_id));
        return true;
    }
}

usage:

// for DSN documentation, check http://php.net/manual/en/ref.pdo-mysql.connection.php
$handler = new MySqlSessionHandler ('mysql:host=mydb.foo.com;dbname=sessions;charset=utf8mb4','MySqlUsername','MySqlPassword');
session_set_save_handler($handler, true);
session_start();
  • now they should definitely share sessions..
  • warning: untested as of writing, but this should work in theory.
hanshenrik
  • 19,904
  • 4
  • 43
  • 89
  • Both domains are on the same physical server as per the question. The `session.save_path` is exactly the same for both (`/var/lib/php/session`) – Andy Jan 28 '19 at 09:24
  • @Andy weird. still, if you change to a sqldb-backed session store, do they share sessions then? re-read my answer, i added an example of sql-db-based session storage which should be compatible with MySQL and MariaDB and PostgreSQL – hanshenrik Jan 28 '19 at 13:22
  • I appreciate you taking the time to answer and providing database session storage code. However, clearly this isn't the solution I'm looking for. I'm asking about sharing the session data *without using a database at all*, particularly as this would require changing application code when this shouldn't be necessary. I know it's possible to read `$_SESSION` between applications on subdomains (without using a database) as I have done it before. However for some reason it isn't working under my current set up and am trying to debug it and work out why. – Andy Jan 28 '19 at 16:03
0

I just try to replicate whit:

  • Product: Plesk Onyx 17.8.11 Update #37
  • PHP Version 7.0.32-0ubuntu0.16.04.1

and works pretty fine. I got values in _Session array in both cases, even I just save the values only in the subdomain.

In my set, the 'Additional configuration directives' for php.ini is the same for the domain and the subdomain.

PaulRM
  • 389
  • 4
  • 13
  • Thanks for testing it out. This doesn't really give me much helpful information though. You're saying it works for you; well it doesn't for me and I'm still none the wiser as to why. – Andy Jan 28 '19 at 09:23