Home » Php » Safety of these methods to encode and decode PHP sessions

Safety of these methods to encode and decode PHP sessions

Posted by: admin July 12, 2020 Leave a comment

Questions:

In a way this question can be seen as an extension of this one.

We’re considering releasing a class that handles deserializing and serializing session data stored in a table on a large scale production website so that we can edit arbitrary session data.

Problem is, session_decode() populates the current $_SESSION without returning a decoded array, and session_encode() doesn’t encode a given array (it only returns a serialized string of the current session.)

The default PHP session serialize handler doesn’t simply use serialize() to encode the sessions, and therefore the only way to get the same functionality of encoding and decoding a session is by either moving the global $_SESSION variable around (i.e store into session, retrieve data and restore) or by trying to reproduce an implementation of what the session.serialize_handler does.

We opted for the latter reproduction approach as it seems less instrusive. There have been a number of attempts at this reproduction in the comments section of session_encode and session_decode in the docs. I’ve picked out two that I think seemed the most reliable and applied them. The decode method seems quite robust but the encode method although it works, was posted over 5 years ago

We’re still reluctant to roll it out simply because there may be unseen edge cases that will cause these methods to break.

Ultimately, I’m looking for:

  • examples that will break the methods below, or
  • reassurance that these methods have been used in production and won’t break
  • perhaps alternatives that have been tried and tested in production?

Thanks everyone in advanced!

The code:

class Session extends BaseSession
{
    /**
     * Taken from http://www.php.net/manual/en/function.session-decode.php#108037
     */
    public function unserialized() {
        $session_data = $this->content;
        $method = ini_get("session.serialize_handler");
        switch ($method) {
            case "php":
                return self::unserialize_php($session_data);
                break;
            case "php_binary":
                return self::unserialize_phpbinary($session_data);
                break;
            default:
                throw new Exception("Unsupported session.serialize_handler: " . $method . ". Supported: php, php_binary");
        }
    }

    /**
     * Taken from http://www.php.net/manual/en/function.session-encode.php#76425
     */
    public function serialize($array, $safe = true) {
        // the session is passed as refernece, even if you dont want it to
        if( $safe ) $array = unserialize(serialize( $array )) ;
        $raw = '' ;
        $line = 0 ;
        $keys = array_keys( $array ) ;
        foreach( $keys as $key ) {
            $value = $array[ $key ] ;
            $line ++ ;
            $raw .= $key .'|' ;
            if( is_array( $value ) && isset( $value['huge_recursion_blocker_we_hope'] )) {
                $raw .= 'R:'. $value['huge_recursion_blocker_we_hope'] . ';' ;
            } else {
                $raw .= serialize( $value ) ;
            }
            $array[$key] = Array( 'huge_recursion_blocker_we_hope' => $line ) ;
        }

        $this->content = $raw;
        $this->save();
    }


    private static function unserialize_php($session_data) {
        $return_data = array();
        $offset = 0;
        while ($offset < strlen($session_data)) {
            if (!strstr(substr($session_data, $offset), "|")) {
                throw new Exception("invalid data, remaining: " . substr($session_data, $offset));
            }
            $pos = strpos($session_data, "|", $offset);
            $num = $pos - $offset;
            $varname = substr($session_data, $offset, $num);
            $offset += $num + 1;
            $data = unserialize(substr($session_data, $offset));
            $return_data[$varname] = $data;
            $offset += strlen(serialize($data));
        }
            return $return_data;
    }

    private static function unserialize_phpbinary($session_data) {
        $return_data = array();
        $offset = 0;
        while ($offset < strlen($session_data)) {
            $num = ord($session_data[$offset]);
            $offset += 1;
            $varname = substr($session_data, $offset, $num);
            $offset += $num;
            $data = unserialize(substr($session_data, $offset));
            $return_data[$varname] = $data;
            $offset += strlen(serialize($data));
        }
        return $return_data;
    }
}
How to&Answers:

Igbinary ( https://github.com/igbinary/igbinary/ ) is a drop in replacement for the standard php serializer. Instead of time and space consuming textual representation, igbinary stores php data structures in compact binary form. Savings are significant when using memcached or similar memory based storages for serialized data. About 50% reduction in storage requirement can be expected. Specific number depends on
your data.

Unserialization performance is at least on par with the standard PHP serializer. Serialization performance depends on the “compact_strings” option which enables duplicate string tracking. String are inserted to a hash table which adds some overhead. In usual scenarios this does not have much significance since usage pattern is “serialize rarely, unserialize often”. With “compact_strings” option igbinary is usually a bit slower than the standard serializer. Without it, a bit faster.

Features

  • Supports same data types as the standard PHP serializer: null, bool, int, float, string, array and objects.
  • __autoload & unserialize_callback_func
  • __sleep & __wakeup
  • Serializable -interface
  • Data portability between platforms (32/64bit, endianess)
  • Tested on Linux amd64, Linux ARM, Mac OSX x86, HP-UX PA-RISC and NetBSD sparc64
  • Hooks up to APC opcode cache as a serialization handler (APC 3.1.7+)
  • Compatible with PHP 5.2 and 5.3

Hope it helps