如何正确结束用户会话?

Mik*_*ike 5 php security csrf owasp

我一直致力于我的网站(PHP)的安全性,并且有很多信息可以摄取.我试图实现我在OWASP上研究过的安全性,但有一件事我有点紧张,除其他外,还有当用户注销时如何处理SESSIONS.

目前我正在使用的是:

session_destroy();
Run Code Online (Sandbox Code Playgroud)

但是,我已经读过我应该更改XRSF令牌并启动另一个SESSION,这样它就会强制用户重新提交登录凭据,从而明确地结束用户SESSION.

是否session_destroy()足够?

编辑

我已经下载了michael-the-messenger,我认为它是由Michael Brooks(Rook)创建的,它应该非常安全,我看到了一些我可能想要使用的代码.这是否可以安全地取代session_destroy()我正在使用的东西?

if($_SESSION['user']->isAuth())
{
    /* if they have clicked log out */
    /* this will kill the session */
    if($_POST['LogMeOut'] == 'true')
    {
        //When the user logs out the xsrf token changes.
        $tmp_xsrf = $_SESSION['user']->getXsrfToken();
        $_SESSION['user']->logout();
        $loginMessage = str_replace($tmp_xsrf, $_SESSION['user']->getXsrfToken(), $loginMessage);
        print layout('Authorization Required', $loginMessage);
    }
    else
    {
        header("Location: inbox.php");
        //user is allowed access. 
    }
}
else
{
    // code goes on ....
Run Code Online (Sandbox Code Playgroud)

登出

public function logout()
{
    $_SESSION['user'] = new auth();
}
Run Code Online (Sandbox Code Playgroud)

显然$_SESSION['user'] = new auth();重新实例化将私有变量设置$auth为false 的对象.

Yan*_*ang 2

但我有点紧张的一件事是,当用户注销时如何处理 SESSIONS。

根据手册

为了完全终止会话,就像注销用户一样,会话 ID 也必须取消设置。如果使用 cookie 来传播会话 ID(默认行为),则必须删除会话 cookie。setcookie() 可以用于此目的。

因此,为了安全地销毁会话,我们还会在客户端计算机上删除它。

session_destroy()一起setcookie(session_name(), null, time() - 86400)会这样做。

除此之外,

你做错了什么以及为什么:

会话存储仅在内部使用数据序列化。通过将对象存储在$_SESSION超全局中,您只需根据需要序列化/反序列化该对象,甚至不知道它。

1)通过存储一个对象,$_SESSION你确实引入了全局状态。$_SESSION是一个超全局数组,因此可以从任何地方访问。

2) 即使通过存储一个保存有关登录用户信息的对象,您也会浪费系统内存。对象表示的长度总是大于字符串的长度。

但你到底为什么要关心包装会话功能呢?出色地,

  • 它使代码易于阅读、维护和测试
  • 它遵循单一职责原则
  • 它避免了全局状态(如果使用得当),您将不会访问会话$_SESSION['foo'],而是$session->read['foo']
  • 您可以轻松更改其行为(例如,如果您决定使用数据库作为会话存储),甚至不会影响应用程序的其他部分。
  • 代码重用能力。您可以将此类用于其他应用程序(或其一部分)

如果将所有与会话相关的功能包装到一个单一类中,那么它将变得很有吸引力:

$session = new SessionStorage();

$session->write( array('foo' => 'bar') );

if ( $session->isValid() === TRUE ) {

    echo $session->read('foo'); // bar

} else {

    // Session hijack. Handle here
}

// To totally destroy a session:
$session->destroy();


// if some part of your application requires a session, then just inject an instance of `SessionStorage`
// like this:
$user = new Profile($session);


// Take this implementation as example:

final class SessionStorage
{
    public function __construct()
    {
        // Don't start again if session is started:
        if ( session_id() != '' ) {
            session_start();
        }

        // Keep initial values
        $_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
        $_SESSION['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
    }

    /**
     * You can prevent majority of hijacks using this method
     * 
     * @return boolean TRUE if session is valid
     */
    public function isValid()
    {
        return $_SESSION['HTTP_USER_AGENT'] === $_SERVER['HTTP_USER_AGENT'] && $_SESSION['REMOTE_ADDR'] === $_SERVER['REMOTE_ADDR'] ;
    }


    public function __destruct()
    {
        session_write_close();
    }

    /**
     * Fixed session_destroy()
     * 
     * @return boolean
     */
    public function destroy()
    {
        // Erase the session name on client side
        setcookie(session_name(), null, time() - 86400);

        // Erase on the server
        return session_destroy();
    }


    public function write(array $data)
    {
        foreach($data as $key => $value) {
            $_SESSION[$key] = $value;
        }
    }


    public function exists()
    {
        foreach(func_get_args() as $arg){

            if ( ! array_key_exists($arg, $_SESSION) ){
                return false;
            }
        }

        return true;
    }

    public function read($key)
    {
        if ( $this->exists($key) ){

            return $_SESSION[$key];

        } else {

            throw new RuntimeException('Cannot access non-existing var ' .$key);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)