php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #70520 session_regenerate_id() "Failed to create session ID" with custom SessionHandler
Submitted: 2015-09-17 14:53 UTC Modified: 2015-10-20 22:34 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: hpdl at oscommerce dot com Assigned: yohgaki (profile)
Status: Closed Package: Session related
PHP Version: 7.0.0RC3 OS: Win10
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: hpdl at oscommerce dot com
New email:
PHP Version: OS:

 

 [2015-09-17 14:53 UTC] hpdl at oscommerce dot com
Description:
------------
session_regenerate_id() ignores a defined session path (via session_save_path()) and uses the windows\temp directory instead.

Did not occur in earlier RC releases.

Test script:
---------------
session_regenerate_id();

Expected result:
----------------
true

Actual result:
--------------
session_regenerate_id(): Failed to create session ID: user (path: C:\WINDOWS\Temp\) in C:\Users\blahblah\blah.php

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-09-17 19:56 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2015-09-17 19:56 UTC] requinix@php.net
Seems to be working for me.

<?php

session_save_path(".");
session_start();

$sid1 = session_id();
$exists1 = file_exists("./sess_{$sid1}");

session_regenerate_id();
$sid2 = session_id();
$exists2 = file_exists("./sess_{$sid2}");

var_dump($exists1); // true
var_dump($sid1 != $sid2); // true
var_dump($exists2); // true

?>

What's your test script? Can you dump session_save_path() before the session_regenerate_id() to make sure it's set appropriately?
 [2015-09-17 23:11 UTC] hpdl at oscommerce dot com
-Status: Feedback +Status: Open
 [2015-09-17 23:11 UTC] hpdl at oscommerce dot com
Sorry, the problem seems to be with custom session handlers.

Below is a snippet that reproduces the problem. The handler used below is taken from the PHP documentation:

http://php.net/manual/en/class.sessionhandler.php

<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', true);

class EncryptedSessionHandler extends SessionHandler
{
    private $key;

    public function __construct($key)
    {
        $this->key = $key;
    }

    public function read($id)
    {
        $data = parent::read($id);

        return @mcrypt_decrypt(MCRYPT_3DES, $this->key, $data, MCRYPT_MODE_ECB);
    }

    public function write($id, $data)
    {
        $data = @mcrypt_encrypt(MCRYPT_3DES, $this->key, $data, MCRYPT_MODE_ECB);

        return parent::write($id, $data);
    }
}

ini_set('session.save_handler', 'files');
$handler = new EncryptedSessionHandler('mykey');
session_set_save_handler($handler, true);

session_start();
echo session_id() . '<br>';
session_regenerate_id(true);
echo session_id();
 [2015-09-18 00:06 UTC] requinix@php.net
-Summary: session_regenerate_id ignores defined session path +Summary: session_regenerate_id() "Failed to create session ID" with custom SessionHandler -Status: Open +Status: Verified -Assigned To: +Assigned To: yohgaki
 [2015-09-18 00:06 UTC] requinix@php.net
Okay, I see it. So rather it's a general failure when overriding SessionHandler::write(), not about the save path.
(Note that the first echo needs to be commented out as it creates output which will interfere.)

Tentatively assigning to @yohgaki as he did some work with session_regenerate_id() before the RC3 release.
 [2015-09-18 08:58 UTC] yohgaki@php.net
It seems Windows only, doesn't it?
I tried the save handler with php-7.0.0RC3 tag and PHP-7.0 branch on my linux box and it works.

Is your session.save_path a local filesystem? i.e. It's not Windows share or like. I know some users are having problem with shared file systems.
 [2015-09-18 09:03 UTC] yohgaki@php.net
I got it right. I'll check it soon.
 [2015-09-18 09:05 UTC] requinix@php.net
I tried it with a session_save_path(".") immediately before the session_start(). CWD was a local directory, and when I ran the code I posted earlier (in the same location) it worked correctly.
 [2015-09-18 09:18 UTC] yohgaki@php.net
-Status: Verified +Status: Analyzed -Type: Bug +Type: Documentation Problem
 [2015-09-18 09:18 UTC] yohgaki@php.net
Found what's wrong. 
Current session save handler expects session read function returns STRING type when there is no error.

However, the code 

    public function read($id)
    {
        $data = parent::read($id);

        return @mcrypt_decrypt(MCRYPT_3DES, $this->key, $data, MCRYPT_MODE_ECB);
    }


returns non string because $this->key is not valid key length.

Apparently, the document has wrong size of key which results mcrypt_decrypt() error. This makes fail to write session data.

Making documentation problem.
 [2015-09-18 09:19 UTC] yohgaki@php.net
s/write/read/
 [2015-09-18 09:21 UTC] yohgaki@php.net
I see error for read, but it will fail on both read/write due to wrong key size.
 [2015-09-18 09:47 UTC] hpdl at oscommerce dot com
Thanks for the pointers here. The error occurred with my database session handler read() method returning a bool false instead of a string. This was only caught in RC3 as earlier 7dev releases and previous PHP versions did not produce any kind of error.

Thanks for the time spent on this.
 [2015-09-18 22:41 UTC] yohgaki@php.net
Since mcrypt seems to have problems https://bugs.php.net/bug.php?id=70529 , I've made new example using OpenSSL AES. Any comments on this new sample code? 

There are too many ini_set() etc, I'll get rid of irrelevant code for the doc later. 

<?php
ob_start();
error_reporting(E_ALL | E_STRICT);
ini_set('session.save_path', '/tmp');
ini_set('display_errors', true);

 /**
  * decrypt AES 256
  *
  * @param data $edata
  * @param string $password
  * @return dencrypted data
  */
function decrypt($edata, $password) {
    $data = base64_decode($edata);
    $salt = substr($data, 0, 16);
    $ct = substr($data, 16);

    $rounds = 3; // depends on key length
    $data00 = $password.$salt;
    $hash = array();
    $hash[0] = hash('sha256', $data00, true);
    $result = $hash[0];
    for ($i = 1; $i < $rounds; $i++) {
        $hash[$i] = hash('sha256', $hash[$i - 1].$data00, true);
        $result .= $hash[$i];
    }
    $key = substr($result, 0, 32);
    $iv  = substr($result, 32,16);

    return openssl_decrypt($ct, 'AES-256-CBC', $key, true, $iv);
  }

/**
 * crypt AES 256
 *
 * @param data $data
 * @param string $password
 * @return base64 encrypted data
 */
function encrypt($data, $password) {
    // Set a random salt
    $salt = openssl_random_pseudo_bytes(16);

    $salted = '';
    $dx = '';
    // Salt the key(32) and iv(16) = 48
    while (strlen($salted) < 48) {
      $dx = hash('sha256', $dx.$password.$salt, true);
      $salted .= $dx;
    }

    $key = substr($salted, 0, 32);
    $iv  = substr($salted, 32,16);

    $encrypted_data = openssl_encrypt($data, 'AES-256-CBC', $key, true, $iv);
    return base64_encode($salt . $encrypted_data);
}

class EncryptedSessionHandler extends SessionHandler
{
    private $key;

    public function __construct($key)
    {
        $this->key = $key;
    }

    public function read($id)
    {
        $data = parent::read($id);

        if ($data === "") {
            return "";
        } else {
            return decrypt($data, $this->key);
        }
    }

    public function write($id, $data)
    {
        $data = encrypt($data, $this->key);

        return parent::write($id, $data);
    }
}

ini_set('session.save_handler', 'files');
$key = substr(sha1(random_bytes(24)), 0, 24);
$handler = new EncryptedSessionHandler($key);
session_set_save_handler($handler, true);

echo '<pre>';
session_start();
$_SESSION['key'] = 1234;
var_dump($_SESSION);
echo session_id() . PHP_EOL;
session_regenerate_id(true);
echo session_id() . PHP_EOL;
 [2015-09-18 22:45 UTC] yohgaki@php.net
Oops. I forgot to modify key string to be static string.
$key = substr(sha1(random_bytes(24)), 0, 24);
should be something like
$key = 'secret_string';
 [2015-09-19 01:41 UTC] yohgaki@php.net
Automatic comment from SVN on behalf of yohgaki
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=337850
Log: Fixed bug #70520 - Update and improve example code
 [2015-09-19 01:42 UTC] yohgaki@php.net
-Status: Analyzed +Status: Closed
 [2015-09-19 01:42 UTC] yohgaki@php.net
New example code is in svn.
 [2015-09-19 06:44 UTC] alec at alec dot pl
I've got the same issue. Don't you think the error is misleading or at least gives no clue about where the issue is?
 [2015-09-19 08:13 UTC] yohgaki@php.net
@alec

When new Zend Engine is introduced, a bug in session read is introduced.

https://bugs.php.net/bug.php?id=70529

I've fixed this today. If you would like to use "binary" session data like mcrypt encrypted data, you'll need most recent PHP7 build.
 [2015-10-19 05:11 UTC] jacky at xsteach dot com
i met the same problem on mac osx with php7RC5. I use yii2 framework with redis as session storage.It is ok when i install php7RC2, but get error session_regenerate_id(): Failed to create session ID: user (path: ). Is this issue fixed after RC3?
 [2015-10-20 22:34 UTC] yohgaki@php.net
@jacky
If you have problem with Redis save handler, please file a new bug with short reproducible code.
 [2020-02-07 06:07 UTC] phpdocbot@php.net
Automatic comment on behalf of yohgaki
Revision: http://git.php.net/?p=doc/en.git;a=commit;h=c63fbfff619bce8eefbbbd93be2e52c2d14311d6
Log: Fixed bug #70520 - Update and improve example code
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Sun Jan 19 06:01:30 2025 UTC