如何正确结束用户会话?

时间:2013-04-28 15:39:42

标签: php security csrf owasp

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

目前我正在使用的是:

session_destroy();

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

session_destroy()是否足够?

修改

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

CODE

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 ....

LOGOUT

public function logout()
{
    $_SESSION['user'] = new auth();
}

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

2 个答案:

答案 0 :(得分:2)

  

但有一点我有点紧张,除其他外,是如何   在用户注销时处理SESSIONS。

根据manual

  

为了完全杀死会话,喜欢将用户注销掉,   会话ID也必须取消设置。如果使用cookie来传播   会话ID(默认行为),然后会话cookie必须是   删除。 setcookie()可以用于此。

因此,为了安全地销毁会话,我们也会在客户机上擦除它。

session_destroy()以及setcookie(session_name(), null, time() - 86400)将会这样做。

除此之外,

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

  

会话存储仅在内部使用数据序列化。通过存储   你刚才做的$_SESSION超全球中的一个对象   在不知道的情况下按需序列化/反序列化该对象。

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

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

但是为什么你甚至应该关心包装会话功能呢?那么,

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

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

$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);
        }
    }

}

答案 1 :(得分:-2)

也许你正在寻找session_unset()。