页面之间的 PHP 会话丢失 - 行为因服务器而异

Posted

技术标签:

【中文标题】页面之间的 PHP 会话丢失 - 行为因服务器而异【英文标题】:PHP sessions lost between pages - behaves differently depending on server 【发布时间】:2016-03-17 07:50:04 【问题描述】:

我花了几个月的时间在我的域上开发一个应用程序。这总体上是一个简单的概念。在开发过程中,我将其托管在自己的域中,但最近将其推送到我们的实际域中。问题是没有在页面之间创建或保留会话,我一生都无法弄清楚原因。

为下面的代码墙道歉,但我更喜欢它而不是理论解释。

让我们从我如何在每一页的顶部开始我的会话:

function sec_session_start() 
    $session_name = 'login';
    $secure = false;
    $httponly = true;

    ini_set('session.use_only_cookies', 1);
    session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly); 
    session_name($session_name);
    session_start();
    session_regenerate_id();

然后我如何检查用户是否已登录。我添加了return x; 而不是false 进行调试。我将此附加到重定向 URL。

function login_check($mysqli) 
if(isset($_SESSION['id'], $_SESSION['login_string'], $_SESSION['type'])) 
    $id = $_SESSION['id'];
    $login_string = $_SESSION['login_string'];

    $user_browser = $_SERVER['HTTP_USER_AGENT'];

    if($_SESSION['type'] == 1 || $_SESSION['type'] == 2) // Admin user
    

        if ($stmt = $mysqli->prepare("SELECT `password` FROM `users` WHERE `id` = ? LIMIT 1"))  
            $stmt->bind_param('s', $id);
            $stmt->execute();
            $stmt->store_result();

            if($stmt->num_rows == 1) 
                $stmt->bind_result($password);
                $stmt->fetch();
                $login_check = hash('sha512', $password.$user_browser);
                if($login_check == $login_string) 
                    return true;
                 else 
                    return 1;
                
             else 
                return 2;
            
         else 
            return 3;
        
     else if($_SESSION['type'] == 3)  // Standard user
        if($stmt=$mysqli->prepare("SELECT `password` FROM `proj` WHERE `id` = ? LIMIT 1"))
        
            $stmt->bind_param("s", $_SESSION['id']);
            $stmt->execute();
            $stmt->store_result();

            if($stmt->num_rows == 1)
            
                $stmt->bind_result($db_key);
                $stmt->fetch();
                $login_check = hash('sha512', $db_key.$user_browser);
                if($login_check == $login_string) 
                    return true;
                 else 
                    return 4;   
                
            
        
     else 
        return 5;   
    
 else 
     return 6;


我有两个登录页面,一个用于管理员,一个用于用户。

管理员:

<?php

ini_set('display_errors','On');
error_reporting(E_ALL);

include_once "../functions.php";
include_once "../db_connect.php"; 

sec_session_start();

if(login_check($mysqli) === true) 

header('Location: ../index.php');


else


// Login form


用户:

<?php

ini_set('display_errors','On');
error_reporting(E_ALL);

include_once "../functions.php";
include_once "../db_connect.php"; 

sec_session_start();

if(login_check($mysqli) === true) 

header('Location: ../index.php');


else


// Login form


完全一样,除了文件来源,因为管理员login.php 位于/admin。尽管如此,第一个正确显示登录表单,而第二个将您重定向到index.php(即使在隐身状态下,对我来说也是个谜),导致重定向循环(因为index.php 将其发回以让 nog 登录) .

除此之外,当我使用正确的凭据登录时,它确实将我定向到 index.php,只是将我重定向回 login.php,错误代码为 6

如果您想了解更多信息或代码示例,请发表评论,我现在迷失在自己的项目中。

任何帮助表示赞赏, 谢谢

12 月 17 日更新:

经过几个小时的调试,我们得出结论,问题不在于代码,而在于服务器配置。一个简单的例子:

<?php

session_start();
echo session_id();

?>

如果您在生产服务器上打开此文件并刷新页面,它会在每个请求中显示一个新的会话 ID。我目前不知道为什么。我已经确认创建了会话文件以及 cookie。它包含正确的信息,并且可以通过具有服务器权限的 SSH 访问。确实有些奇怪的行为。

有什么线索吗?

【问题讨论】:

确保每个请求只启动一次会话,例如,如果多个文件在同一个请求中调用sec_session_start(),会发生什么? 不同的服务器配置会以不同的方式处理会话,例如通过 cookie 或文件等。所以会话的初始化方式会有所不同,这些只是需要考虑的一般 cmets 1) 您是否在不同的服务器上设置了本地php.inisession.savepath 等设置具有帐户特定地址,这些地址将在您的测试和生产服务器上更改。 2) 你的两台服务器是否安装了相同的 PHP 版本? 3)运行phpinfo,比较两台服务器上的输出设置,比较差异(如会话相关地址)。这些有没有给你任何好奇的答案? 您存储会话的服务器地址是否已正确设置和启用?如具有正确的权限,并且该文件夹存在等?这些都是目前我正在运行的简单试错调试 感谢您的 cmets。 1)我在我的问题中没有提到的是它托管在一个子域上,如果有任何兴趣的话。在主域上托管了一个 WordPress,它运行良好。这会以某种方式影响会话路径吗? 2) 是的,或者非常相似。 3)我会试试这个并寻找差异!关于会话路径,我首先使用session_get_cookie_params() 并将其用作保存路径和域。现在的情况只是我第一次尝试解决问题的结果。 【参考方案1】:

我也遇到了这个问题,但它是 ssl 安装,它用 https 更改了基本文件链接,并且发送文件没有 ssl 链接。我也想到了会话长度。除此之外,我作为您的托管服务提供商可以解释这一点。

【讨论】:

【参考方案2】:

我过去有过类似的经历,我不记得我是如何解决这个问题的,但据我所知,它与会话cookie可变顺序有关> 在 PHP 中。

在 php.ini 中寻找下面这一行,

variables_order = "GPCS"

不知何故,更改这些变量的顺序会影响 PHP 会话。

如果通过任何更改,像我一样,您无法更改服务器上的设置,您也可以尝试使用常规 cookie 复制会话 cookie:

session_start();
set_cookie(session_name(), session_id());

另外注意,不同的浏览器对会话 cookie 也有不同的行为,例如:safari。在 safari 上,无论变量顺序如何,我都必须复制 cookie。

希望对你有帮助

【讨论】:

【参考方案3】:

几个月前,我在生产服务器上遇到了类似的问题,其中会话即将过期(例如在您的情况下,会话在页面之间丢失)并且用户突然从我的网站注销。因此,经过几天的调试,一切都归结为 php.ini 文件中的这三行。

session.gc_divisor session.gc_maxlifetime session.gc_probability

来自the manual,

session.gc_probability 与 session.gc_divisor 一起用于管理 gc(垃圾收集)例程启动的概率。默认为 1。

session.gc_divisor 加上 session.gc_probability 定义了每次会话初始化时 gc(垃圾收集)进程启动的概率。概率是通过使用 gc_probability/gc_divisor 计算的,例如1/100 表示 GC 进程有 1% 的机会在每个请求上启动。 session.gc_divisor 默认为 100。

session.gc_maxlifetime 指定数据将被视为“垃圾”并可能被清除的秒数。垃圾收集可能在会话开始期间发生(取决于 session.gc_probability 和 session.gc_divisor)。

这些是我在 php.ini 文件中的原始会话配置,实际上首先造成了整个麻烦,

session.gc_divisor 1000 1000
session.gc_maxlifetime 1440 1440
session.gc_probability 0 0

所以这意味着垃圾收集器将以 session.gc_probability 除以 session.gc_divisor 的概率开始。并使用该选项的默认值(分别为 0 和 1000),垃圾收集概率 = session.gc_probability/session.gc_divisor 即 0/1000 = 0%,我认为这是不正确的,加上 session.gc_maxlifetime 对我来说似乎太低了.

所以,我决定在我的 php.ini 文件中更改会话配置并包含这些值,

session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 28800

从那以后它工作正常。 所以我给你的建议是,检查你的 php.ini 文件中的这三个值,如果需要,根据你的需要更改它们。

【讨论】:

不幸的是,这并没有改变任何东西。我用今天的发现更新了我的问题。 请原谅..!!【参考方案4】:

我浏览了你的代码,除了一件事之外,我没有发现任何不寻常的地方。

您永远不应该使用== 进行字符串比较=== 可以。

$something = 0;
echo ('password123' == $something) ? 'true' : 'false';

运行上面的代码,你会发现会话丢失的原因。在您的函数 login_check 中,您使用 == 比较两个字符串

 if($login_check == $login_string)

替换为:

 if($login_check === $login_string)

其他一切都很好。如果更改这个小东西不能解决您的问题,请告诉我。

建议

您在会话开始之前连接到数据库。我建议您导入您的函数,然后开始您的会话,然后连接到您的数据库。

include_once "../functions.php";
sec_session_start();
include_once "../db_connect.php"; 

【讨论】:

明天我会尽快尝试这个。如果这是解决方案,而且很可能是这样,为什么它在两个域之间的行为不同? 你能贴出关于如何设置会话的代码吗?由于这里的事情是正确的,那么你必须错误地设置会话 你具体指的是什么代码?我将会话设置在我发布的第一个括号中,然后在后面的括号中开始。 能否在 session_set_cookie_params() 之前先尝试使用 session_name() 您可以尝试在 session_set_cookie_params() 之前先使用 session_name() 吗?它有时会导致错误。请检查它【参考方案5】:

检查会话是否已经开始并确保它只完成一次:

使用 PHP >= 5.4:

function sec_session_start() 
    if (session_status() == PHP_SESSION_NONE) 
        $session_name = 'login';
        $secure = false;
        $httponly = true;

        ini_set('session.use_only_cookies', 1);
        session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly); 
        session_name($session_name);
        session_start();
        session_regenerate_id();
    

PHP 5.4 之前的版本:

function sec_session_start() 
    if (session_id() == '') 
        $session_name = 'login';
        $secure = false;
        $httponly = true;

        ini_set('session.use_only_cookies', 1);
        session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly); 
        session_name($session_name);
        session_start();
        session_regenerate_id();
    

另外,为什么每次都要重新生成会话?

【讨论】:

我会试试这个,但两台服务器都是最新的,在这种情况下应该没有区别。关于再生:它目前对每个请求都执行,因为它是保持其安全的简单方法。话虽如此,它可以减少到不是每个新的请求。我不认为请求的数量是这里的问题,而且在我之前使用过代码的之前的案例中也没有。

以上是关于页面之间的 PHP 会话丢失 - 行为因服务器而异的主要内容,如果未能解决你的问题,请参考以下文章

UIScrollView 的行为是不是因全屏而异?

Python argparse 行为因启动模式而异(Windows)

php - 刷新页面后会话丢失[重复]

控制台命令上的PHP会话丢失

会话控制

php会话数据有时会丢失