PHP 7用户会话问题 - 初始化存储模块失败

8
在使用PHP 7.0版本时,与各种PHP框架的会话驱动程序存在漏洞。我最初在使用CodeIgniter数据库驱动程序时遇到了这个问题,并认为这是一个CodeIgniter的问题,但后来我在多个会话驱动程序和多个框架中都遇到了这个问题。此时,我已经安全地得出结论,会话驱动程序的类型是无关紧要的——似乎在随机情况下,应用程序将崩溃并且日志(我尝试过Apache和php-fpm + nginx)填满以下内容:
PHP致命错误:session_start():无法初始化存储模块:用户(路径:[我在php.ini路径中设置的任何内容])
我正在使用的驱动程序不使用php.ini中设置的值,无论我是否在php.ini中将session.save_handler设置为files、redis等,也无论我设置的路径是什么(如果是redis,则为Redis服务器,启用文件的完全可写文件夹),都会出现错误。这里的路径不应该被访问,除非在框架外调用了本地的“session_start()”。此外,在框架外调用“session_start()”可以正常工作……因此显然PHP可以访问该路径。就好像在某些时候,会话驱动程序成为了框架驱动程序和php.ini中设置的任何内容的混合体。错误消息总是将“user”作为session.save_handler,因此显然这不是从php.ini中提取的……但路径是。为什么会发生这种情况?这是一种难以描述的问题,直到你经历过它……而且很难复制,因为即使对于数百个会话,所有东西似乎都正常工作(直到突然停止工作)。重启Apache也无法纠正这个问题——这里有许多问题,我最终只能重新启动机器以避免停机时间。显然,PHP 7机器现在将被从负载均衡器轮换中移除……但我希望现在已经解决了问题。
我在自己编译的PHP 7.0 RC5、RC6和RC8上以及Ubuntu 15.10 Wily(7.0.0-2+deb.sury.org~wily+1)上的最新Ondřej Surý PPA上遇到了这个问题。我在CodeIgniter和Symfony上遇到了这个问题,并且无论框架中使用的驱动程序类型(文件、数据库、redis)或在php.ini中设置的session.save_handler如何(应该与此无关,但只是想提一下),我都遇到了这个问题。我不断尝试各种组合并将事物扔到野外,但这个问题每次都会发生(有时需要12个小时以上,具体取决于网站的流量)。
感谢您提供的任何帮助!我愿意尝试任何建议。

你是否已经在 http://bugs.php.net/ 上报告了这个错误? - Andrea
只需添加正确的路径来存储会话。 例如:进入配置文件并替换 $config['sess_save_path'] = NULL; 为 $config['sess_save_path'] = APPPATH . 'cache/session/';别忘了将Cache文件夹设置为0777权限。 - Ricky Harerri
14个回答

8
当会话处理程序的open()函数未返回布尔值TRUE时,就会发生此错误,这显然意味着某种失败。
这可能是连接数据库失败、打开文件失败、不存在的目录等——这取决于会话处理程序实际使用的内容。

这是正确的答案。在我的情况下,我添加了错误的数据库名称。 - rafaelmorais
我同意。这是正确的回答。在我的情况下,我的服务器数据库没有工作,但Code Ignitor给了我与此处讨论的相同错误。服务器支持一直试图说服我这是你脚本中的错误。但我证明了DB已经崩溃了,他们意识到了这一点。 - Rooturaj Pattanaik

6

_open函数应该返回true以避免此错误。

无论我们使用数据库还是文件,它都不能为null或为空。

当我们使用数据库存储会话数据时,我们保持其为空或不返回布尔值。这是此错误的主要原因。

class session_handler
{
    public function __construct()
    {
        session_set_save_handler(
            array($this, "_open"),
            array($this, "_close"),
            array($this, "_read"),
            array($this, "_write"),
            array($this, "_destroy"),
            array($this, "_gc")
        );
    }

    public function _open($savePath, $sessionId)
    {
        return true;
    }

    public function _close() {  }    
    public function _read($id) {  }
    public function _write($id, $data) {  }
    public function _destroy($id) {  }
    public function _gc($max) {  }
}

这只适用于PHP 7。我不知道它是否是错误。


1
实际上,这是一个错误,在PHP 5中并没有发生,只是人们习惯了错误的行为,所以直到PHP 7才修复它,这是一个有影响力的向后兼容性问题。 - Narf
1
这也解决了我在ZF1上的一个问题。谢谢。 - Felipe Francisco
嘿,你能帮我一下吗?你说的是哪个文件?我在CodeIgniter中找不到session_handler文件。 - runningmark
我认为SessionHandlerInterface的实现中没有使用下划线来命名函数。我认为它们只是 "open"、"close"、"write" 等,没有下划线。 - Everett

4

通常情况下,这是由于会话无法连接到其使用的任何驱动程序(文件、数据库)所致。

我通过查看config.php文件解决了这个问题。

    $config['sess_driver'] = 'database';
    $config['sess_cookie_name'] = 'ci_session';
    $config['sess_expiration'] = 7200;
    $config['sess_save_path'] = 'ci_sessions';
    $config['sess_match_ip'] = FALSE;
    $config['sess_time_to_update'] = 300;
    $config['sess_regenerate_destroy'] = FALSE;

因为我使用了数据库,这意味着出于某种原因它无法连接!(对于文件,可能是没有写入缓存文件的权限)

接下来,我打开了日志记录。

    $config['log_threshold'] = 4;

如果 4 = 所有信息 ,那么刷新页面,现在我在 application/logs 目录下找到了一个名为 log-#.php 的文件,其中的 # 是日期。

    INFO - 2017-08-30 10:05:41 --> Database Driver Class Initialized
    ERROR - 2017-08-30 10:05:41 --> Severity: Warning --> mysqli::real_connect(): (HY000/1045): Access denied for user 'user'@'localhost' (using password: YES) /var/system/database/drivers/mysqli/mysqli_driver.php 202
    ERROR - 2017-08-30 10:05:41 --> Unable to connect to the database
    ERROR - 2017-08-30 10:05:41 --> Severity: Warning --> mysqli::real_connect(): (HY000/1045): Access denied for user 'user'@'localhost' (using password: YES) var/system/database/drivers/mysqli/mysqli_driver.php 202
    ERROR - 2017-08-30 10:05:41 --> Severity: Error --> session_start(): Failed to initialize storage module: user (path: ci_sessions) /var/system/libraries/Session/Session.php 140

所以我只需确保用户可以访问数据库,然后它就可以工作。


只需添加正确的路径来存储会话。 例如:进入配置文件并替换$config['sess_save_path'] = NULL; 为 $config['sess_save_path'] = APPPATH . 'cache/session/';别忘了将Cache文件夹设置为0777权限。 - Ricky Harerri
成功了!谢谢! - arefindev

3

它运行得很完美。

$config['sess_driver'] = 'files';
$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = sys_get_temp_dir();
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;

除了在Rocky Linux上使用这种方法之外,其他方法都没有起作用。 - AbsoluteDev

2

我决定迁移我的系统并进行一些必要的更新。在使用Codeigniter 3和php 7.2时,我遇到了这个问题。当我发现这个问题后,我意识到它是多么荒谬,并想知道为什么我没有早点发现它。

无论如何,这是至少对我有效的解决方案。

显然,确保已安装memcacheD:

sudo apt-get update
sudo apt-get install php7.2-memcached

如果一切顺利,我们可以继续检查其他事项。
在Codeigniter的配置文件"/application/config/config.php"中,有一个部分用于指定会话选项:
$config['sess_driver'] = 'memcached';
$config['sess_cookie_name'] = 'some_session_name';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = NULL;
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;

对我而言需要更改的代码行是这一行:

$config['sess_save_path'] = NULL;

请确保将此设置为CI3手册中记录的有效路径。正确的设置应该如下所示:

$config['sess_driver'] = 'memcached';
$config['sess_save_path'] = 'localhost:11211';

请确保将“localhost”更改为反映您的memcached服务器位置。

有关更多信息,请查看-Codeignier会话手册

还可以在此处查看:php memcacheD会话支持

如果页面上的评论被删除,我会在这里发布:

如果要使用'memcacheD'扩展而不是'memcache'(有两个不同的扩展名)进行会话控制,则应注意修改php.ini文件。

大多数谷歌网页资源都是基于memcache,因为它比memcacheD早。他们会说如下:

session.save_handler = memcache session.save_path =   "tcp://localhost:11211"

但是对于memcacheD来说,这是无效的

您应该像这样修改php.ini:

session.save_handler = memcached session.save_path = "localhost:11211"

看,没有协议标识符

为了测试,我做了这个,以确保php本身没有遇到访问memcacheD的问题:

session_start();

    header('Content-Type: text/plain');
    session_start();
    if(!isset($_SESSION['visit']))
    {
        echo "This is the first time you're visiting this server\n";
        $_SESSION['visit'] = 0;
    }
    else
            echo "Your number of visits: ".$_SESSION['visit'] . "\n";

    $_SESSION['visit']++;

    echo "Server IP: ".$_SERVER['SERVER_ADDR'] . "\n";
    echo "Client IP: ".$_SERVER['REMOTE_ADDR'] . "\n";
    print_r($_COOKIE);

$servers = explode(",", ini_get("session.save_path"));
$c = count($servers);
for ($i = 0; $i < $c; ++$i) {
  $servers[$i] = explode(":", $servers[$i]);
}


$mem = new memcached();

$mem->addServer('127.0.0.1', '11211', '1');
$mem->set('011', 'Hello There');

print_r($mem->get('011'));

print_r($mem->getAllKeys());

这让我看到memcacheD工作正常。

此外,在您的php.ini文件中,有一些需要注意的选项。只需搜索[session]或session.save_path即可。CI3表示它不使用php.ini文件中的此选项,但如果您计划在框架外部使用memcacheD并保持一致性,则值得设置。

此选项从php.ini文件的第1327行左右开始:

[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
session.save_handler = memcached

; Argument passed to save_handler.  In the case of files, this is the path
; where data files are stored. Note: Windows users have to change this
; variable in order to use PHP's session functions.
;
; The path can be defined as:
;
;     session.save_path = "N;/path"
;
; where N is an integer.  Instead of storing all the session files in
; /path, what this will do is use subdirectories N-levels deep, and
; store the session data in those directories.  This is useful if
; your OS has problems with many files in one directory and is
; a more efficient layout for servers that handle many sessions.
;
; NOTE 1: PHP will not create this directory structure automatically.
;         You can use the script in the ext/session dir for that purpose.
; NOTE 2: See the section on garbage collection below if you choose to
;         use subdirectories for session storage
;
; The file storage module creates files using mode 600 by default.
; You can change that by using
;
;     session.save_path = "N;MODE;/path"
;
; where MODE is the octal representation of the mode. Note that this
; does not overwrite the process's umask.
; http://php.net/session.save-path
;session.save_path = "/var/lib/php/sessions"
session.save_path = "locahost:11211"

检查设置的另一个地方是mecacheD服务器配置文件。根据系统,其位置可能会有所不同。对我而言,它位于/etc/目录下。

/etc/memcached.conf

以下是我遇到的错误的参考图:

错误图片

编辑

如果您正在使用socket,例如unix:///some/place/memcached.sock连接到Memcached服务器,则没有文档说明如何使其工作。您只需在配置中执行以下操作:

$config['sess_save_path'] = "/some/place/memcached.sock:11211";

1

刚刚将/your-Codeigniter-project/system/cache及其内容的权限更改为0777,它就可以工作了。实际上,由于缺少权限,无法创建会话文件。使用以下命令:sudo chmod 0777 /your-Codeigniter-project/system/cache sudo chmod 0777 /your-Codeigniter-project/system/cache/* 注意:请将上面的“your-Codeigniter-project”替换为您的项目路径。


1
在我的情况下,我使用的是 PHP ver 5.6CI ver 3.4 (Codeigniter)。 服务器: BIGROCK 我在我的生产服务器上发现了这些问题。
然后经过很长时间后,我尝试删除我的数据库并重新创建它。
然后我才知道我没有给用户访问数据库的权限因此,在您完成创建数据库和用户之后,您应该为用户授予权限(特权)以访问数据库。 如果在您的情况下有用,请告诉我。谢谢。

1
  1. 使用phpinfo()函数在yourdomain.com上查找默认的会话临时文件夹(session.save_path)
  2. 通过ssh连接到您的主机
  3. 进入tmp父文件夹 $ cd /home/abc/xyz
  4. 更改权限为777 -R tmp

0

我更改了我的php.ini文件:

session.save_path = "N;/path" **to** session.save_path = "/tmp"

然后我重新启动了我的php7-fpm


我可以在普通的php文件中(框架之外)发出“session_start()”,没有任何问题...所以即使是php.ini中的配置也可以正常工作。更令人沮丧的是,即使使用框架会话驱动程序,一切都能正常工作(直到不能)-错误日志从空白变成了同样的错误,一秒钟重复几十次:PHP致命错误:session_start():无法初始化存储模块:用户(路径:tcp://127.0.0.1:6379) - DrBlueSpruce
只是为了澄清,上述错误是当我将php.ini更改为使用Redis作为save_handler时出现的,相应的位置是在php.ini中设置的。然而,在应用程序中实际使用的会话驱动程序并不是Redis...所以不知何故,在某个时候,会话驱动程序似乎部分地恢复到了php.ini的设置。我会继续深入挖掘 - 谢谢。 - DrBlueSpruce
出于安全考虑,您的 session.save_path 不应该对除 PHP 以外的任何人可写。/tmp/ 通常是所有人都可写的。 - Andrew Ensley

0

我不得不将我的CodeIgniter升级到最新版本来解决这个问题。

运行在:

CentoOS: 7.6.1810
Mysql: 8.0.15
PHP: PHP 7.3.4 

我尝试了很多事情,比如将会话从数据库移动到文件和Memcached中。但是没有任何效果。唯一解决这个问题的方法是从CI 3.0升级到3.1,然后它就像应该做的那样工作了。

希望能对某些人有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接