Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP7 is breaking my sessions when custom session handler is used and a seccond postgres connection is made

While using a custom session handler to save sessions on a postgreSQL server open a seccond postgres connection and the session will break if any unicode chars are present!

This is my custom session handler:

class custom_session_handler implements SessionHandlerInterface {

    protected $nombre_de_sesion, $tiempo_de_vida, $db;

    public function open($savePath, $sessionName) {
        $this->db = pg_connect("host=**** port=5432 dbname=**** user=**** password=***") or die('Imposible conectar con la base de datos de sesiones');
        $this->gc(time());
        $this->nombre_de_sesion = trim($sessionName);
        $this->tiempo_de_vida = pg_escape_literal(time() + 2500);
        return (isset($this->nombre_de_sesion) && strlen($this->nombre_de_sesion) > 2) ? true : false;
    }

    public function close() {
        return pg_close($this->db);
    }

    public function destroy($session_id) {
        return pg_affected_rows(pg_query($this->db, 'DELETE FROM "sesiones_soporte" WHERE session_id = ' . pg_escape_literal($session_id) . '')) ? true : false;
    }

    public function write($session_id, $session_data) {
        $escaped_id = pg_escape_literal($session_id);
        $escaped_session = pg_escape_literal(pg_escape_bytea($session_data));
        if (pg_affected_rows(pg_query($this->db, 'UPDATE "sesiones_soporte" SET "session_expira" = ' . $this->tiempo_de_vida . ', "session_byte"=' . $escaped_session . ' WHERE session_id = ' . $escaped_id . ' AND session_expira > ' . (int) time())) == 1) {
            return true;
        } else {
            pg_query($this->db, 'INSERT INTO "sesiones_soporte" ("session_id", "session_expira", "session_byte") VALUES (' . $escaped_id . ', ' . $this->tiempo_de_vida . ', ' . $escaped_session . ' )');
            return true;
        }
        return false;
    }

    public function read($session_id) {
        $escaped_id = pg_escape_literal($session_id);
        $sesion = pg_unescape_bytea(pg_fetch_result(pg_query($this->db, 'SELECT session_byte FROM "sesiones_soporte" WHERE session_id = ' . $escaped_id . ' AND session_expira > ' . (int) time() . ' LIMIT 1'), 0, 'session_byte'));
        !isset($sesion) ? : pg_query($this->db, 'UPDATE "sesiones_soporte" SET  "session_expira" = ' . $this->tiempo_de_vida . ' WHERE session_id = ' . $escaped_id . ' AND session_expira > ' . (int) time());
        return (isset($sesion) ? $sesion : false);
    }

    public function gc($maxlifetime) {
        return pg_affected_rows(pg_query($this->db, 'DELETE FROM "sesiones_soporte" WHERE session_expira < ' . (int) $maxlifetime));
    }

}

Init our handler along with our session:

$handler = new custom_session_handler();
session_set_save_handler($handler, true);
session_name('my_session');
session_start();

And save some data into our session:

    $_SESSION['test'] = 'áéíóúñ';

At this point you can var_dump() your session all that you want and it will work, you can refresh the page and it will maintain session info and it will work but stay with me on this...

well, I need to check the fridge:

$seccond_server = pg_connect("host=#### port=#### dbname=#### user=#### password=######### or die("No bueno on db #2");
    print_r($_SESSION);

Oooh, session data seems ok but... wait... Now your session is broken!

Reload the page and you will get: PHP Warning: session_start(): Failed to decode session object. Session has been destroyed in some location at some line

Reloading the page once more will give you: PHP Warning: pg_fetch_result(): Unable to jump to row 0 on PostgreSQL result index something in some path:

You can play commenting and uncommenting that seccond pg_connect and the session will work as long as you dont do that seccond pg_connect

Updates:

  • I forgot to mention that the same code works flawlessly on PHP 5.6.2

  • I added this problem as a comment to the PHP bug tracking system as I found something similar there: https://bugs.php.net/bug.php?id=70584

  • I just filled in a PHP bug report if anyone wants to follow and/or update it: https://bugs.php.net/bug.php?id=71088

  • Client and server info:

    FIRST SERVER (session storage): PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit

    SECCOND SERVER: PostgreSQL 8.4.20 on x86_64-redhat-linux-gnu, compiled by GCC gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit

    CLIENT(php): PostgreSQL(libpq) Version 9.4.4

like image 465
Solrac Avatar asked Oct 18 '22 20:10

Solrac


1 Answers

The real problem:

BYTEA escaping use diffrent escaping methods when it comes about postgres 9 and postgres 8 and PHP does not isolate pg_ behavior @ class level.

The real answer:

You need to pass the right database link as the first argument of pg_escape_bytea() so it can use the right escape method:

pg_escape_bytea($this->db, $data);

On the other hand pg_unescape() wont take your db link as an argument.

A Workaround that works (you shouldn't do this):

You can of course wrap up the session info in base64() and then escape it as bytea. Later on you will need to unescape and unwrap it.

Seems to be that the escaping methods are diffrent in how they handle multibyte strings and therefore, a base64 string wont produce any error.

Extra info:

yohgaki who is assigned to the php bug ticket pointed out diffrent errors on my session handler, he also posted and example of a well implemented session handler using external databases. If you are intrested the full details are here: https://bugs.php.net/bug.php?id=71088

like image 193
Solrac Avatar answered Oct 21 '22 17:10

Solrac