会话ID重新生成无法在Windows

时间:2016-08-24 23:27:42

标签: php session

我有以下代码在我尝试过的几种linux风格下工作正常(Ubuntu,Debian 8,CentOS 7),但是当我在Windows上使用它时,重新生成失败而没有错误。 $duration是表示为static $duration = 60 * SESSION_TIMEOUT;的类中的值,其中SESSION_TIMEOUTconfig.inc.php文件(用户设置)中定义的常量。

session_start();
self::csrf(false);
if(self::verify(false) === true) {
    $_SESSION['expires']    =   time() + self::$duration;
}
session_regenerate_id(true);
$id = session_id();
session_write_close();
session_id($id);
session_start();

如果我在行$id之后回显$id = session_id();,则会有一个值,但如果我在最后session_id()之后回显session_start(),则为空。 (如果您认为此方法是“狡猾的'”

,请参阅session_regenerate_id

我不知道为什么这个再生代码失败了:

session_regenerate_id(true);
$id = session_id();

// one of the lines below this are causing session_id() to be blank
session_write_close();
session_id($id);
session_start();

请帮助我确定当新会话应包含新会话ID时导致新会话空白的原因。

  

session.save_path = 2; 755; d:/server/www/127.0.0.1/sessions

更新:我能够在日志中找到它,但有趣的部分是会话文件是用数据创建的。

  

PHP警告:未知:无法写入会话数据(文件)。请在第0行的Unknown中验证session.save_path的当前设置是否正确(2; 755; d:/server/www/127.0.0.1/sessions)

根据要求,在调用$id(及后续代码)之前,session_write_close()的示例为9vdom0ghuqkvsdcnkjacurc6rf2n4fn1s1gvfva44okd15jdpm30

更新

经过一些测试后,我发现这个问题比上面提到的要复杂得多。我最初认为的问题是会话重新生成问题,更多的是没有存储会话变量的问题。我相信这仍然与Windows上的会话重新生成有关,但是我仍然无法确定修复,因为这段代码在Linux上运行得非常好。

这是班级(缩小):

class session {

    static $duration = 60 * SESSION_TIMEOUT;

    public static function start() {
        session_start();
        self::csrf(false);
        if(self::verify(false) === true) {
            $_SESSION['expires']    =   time() + self::$duration;
        }
        session_regenerate_id(true);
        $id = session_id();
        session_write_close();
        session_id($id);
        session_start();
    }

    public static function csrf($new = false) {
        if(!isset($_SESSION['csrf']) || $new === true) {
            $_SESSION['csrf']       = sha1(openssl_random_pseudo_bytes(mt_rand(16,32)));
        }
        return $_SESSION['csrf'];
    }

}

用于调用它的代码相当复杂(当涉及到确定何时生成新的csrf标记时),但下面是我尝试从整个项目中提取一个块以演示其用法。后面的login代码应该足以让我相信查看逻辑:

if( session::verify() === true ) {

    redirect();

} else {

    $error = '';
    $token = $_SESSION['csrf'];

    if ( !empty($_POST) ) {
        if ( isset($_POST['csrf']) ) {
            // check CSRF token and if match ti token stored in session
            $csrf       =   trim(filter_input(INPUT_POST, 'csrf', FILTER_SANITIZE_SPECIAL_CHARS, array('flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH)));
            if($csrf != $_SESSION['csrf']) { session::destroy(); $error = 'CSRF Attack Detected'; }

            if( isset($_POST['username']) && isset($_POST['password']) && empty($error) ) {
                // login validation code

                if($auth) {
                    // regenerate csrf token
                    $token = session::csrf(true);
                    session::create( $username );

                    // redirect back to application root
                    redirect();
                }
                $error = 'Invalid credentials';
            } else {
                if(empty($error)) { $error = 'Invalid credentials'; }
            }

            // user was not authenticated, regenerate csrf token to prevent form spam
            $token = session::csrf(true);

        } else {
            // CSRF token did not match stored token in session
            $error = 'CSRF Attack Detected';
        }
    }

}

登录代码中使用的会话类的其他方法如下:

使用用户信息更新会话数据。

public static function create($user) {
    $_SESSION['nonce']      =   sha1(microtime(true));
    $_SESSION['ip']         =   $_SERVER['REMOTE_ADDR'];
    $_SESSION['agent']      =   sha1($_SERVER['HTTP_USER_AGENT']);
    $_SESSION['expires']    =   time() + self::$duration;
    $_SESSION['user']       =   $user;
    session_regenerate_id(true);
    $id = session_id();
    session_write_close();
    session_id($id);
    session_start();
}

验证发布的信息是否与服务器会话相匹配

public static function verify($destroy = false) {
    $valid = true;
    try {
        if( !isset($_SESSION['nonce']) ) { $valid = false; }
        if( !isset($_SESSION['user']) ) { $valid = false; }
        if( isset($_SESSION['ip']) ) { if($_SESSION['ip'] != $_SERVER['REMOTE_ADDR']) { $valid = false; } } else { $valid = false; }
        if( isset($_SESSION['agent']) ) { if($_SESSION['agent'] != sha1($_SERVER['HTTP_USER_AGENT']) ) { $valid = false; } } else { $valid = false; }
        if( isset($_SESSION['expires']) ) { if($_SESSION['expires'] <= time()) { $valid = false; } } else { $valid = false; }
    } catch (Exception $e) {
        $valid = false;
    }
    if($valid === false) {
        if(isset($_SESSION['nonce'])) { unset($_SESSION['nonce']); }
        if(isset($_SESSION['ip'])) { unset($_SESSION['ip']); }
        if(isset($_SESSION['agent'])) { unset($_SESSION['agent']); }
        if(isset($_SESSION['expires'])) { unset($_SESSION['expires']); }
        if(isset($_SESSION['user'])) { unset($_SESSION['user']); }
        if($destroy === true) {
            session_unset();
            session_destroy();
        }
    }
    return $valid;
}

更新

通过运行一个简单的测试来缩小范围:

// ensure sessions are writeable
if(!is_writable(session_save_path())) {
    // try alternate session path
    $_session_save_path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'sessions';
    if(!is_writable($_session_save_path)) {
        echo "Can't write sessions"; exit;
    } else {
        session_save_path(
            $_session_save_path
        );
    }
}

结果&#39;无法写会话&#39;这意味着所有尝试和写入数据的方法都失败了。

这里有趣的是,无论我做什么(甚至将驱动器的根目录设置为“拥有&#39;完全&#39;权限”,所有子文件夹和文件似乎都有一个永久的半阴影“只读”复选框。除此之外,file_put_contents("hello.txt", "hello"); exit();在本地网站的根文件夹中工作,但不在2个目录下的任何子文件夹中工作 - 即使在分配之后(和检查)权限。(例如d:\ server \ websites \ 127.0.0.1 \ htdocs \ works,但不是d:\ server \ websites \ 127.0.0.1 \ htdocs \ path1 \ path2 \

更新

做了一些更多的故障排除,兔子洞变深了。找一个脚本来查找运行PHP的用户,以便更好地帮助检查权限:

echo 'Current script owner: ' . get_current_user();

回应&#39;当前脚本所有者SYSTEM&#39;

SYSTEM permissions

我跟进了这段代码:

$new = false;

session_start();
if(!isset($_SESSION['csrf']) || $new === true) {
    $_SESSION['csrf']       = sha1(openssl_random_pseudo_bytes(mt_rand(16,32)));
}
session_regenerate_id(true);
$id = session_id();
session_write_close();
session_id($id);
session_start();

哪个仍然失败。我确实尝试了一些最终有用的东西,但我真的不喜欢它,因为它涉及不设置php.ini文件中记录的安全位。

我改变了:

session.save_path = "2;755;d:/server/www/127.0.0.1/sessions"

session.save_path = "d:/server/www/127.0.0.1/sessions"

这样可行,但所有会话文件现在都可以访问。

(服务器是在配置了相同php.ini文件的所有平台上的apache和php的相同版本,服务器配置文件尽可能接近)

0 个答案:

没有答案