0

I have a web app which I am testing, after loging out my admin account and log in with a normal user, I get redirected to my admin account! I tried different browsers, cleaned all browser data, etc to no avail. This poses a security hazard and would like to fix it ASAP.

Session.php:

namespace OAuth\Common\Storage;

use OAuth\Common\Token\TokenInterface;
use OAuth\Common\Storage\Exception\TokenNotFoundException;
use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException;

/**
 * Stores a token in a PHP session.
 */
class Session implements TokenStorageInterface
{
    /**
     * @var bool
     */
    protected $startSession;

    /**
     * @var string
     */
    protected $sessionVariableName;

    /**
     * @var string
     */
    protected $stateVariableName;

    /**
     * @param bool $startSession Whether or not to start the session upon construction.
     * @param string $sessionVariableName the variable name to use within the _SESSION superglobal
     * @param string $stateVariableName
     */
    public function __construct(
        $startSession = true,
        $sessionVariableName = 'lusitanian-oauth-token',
        $stateVariableName = 'lusitanian-oauth-state'
    ) {
        if ($startSession && !$this->sessionHasStarted()) {
            session_start();
        }

        $this->startSession = $startSession;
        $this->sessionVariableName = $sessionVariableName;
        $this->stateVariableName = $stateVariableName;
        if (!isset($_SESSION[$sessionVariableName])) {
            $_SESSION[$sessionVariableName] = array();
        }
        if (!isset($_SESSION[$stateVariableName])) {
            $_SESSION[$stateVariableName] = array();
        }
    }

    /**
     * {@inheritDoc}
     */
    public function retrieveAccessToken($service)
    {
        if ($this->hasAccessToken($service)) {
            return unserialize($_SESSION[$this->sessionVariableName][$service]);
        }

        throw new TokenNotFoundException('Token not found in session, are you sure you stored it?');
    }

    /**
     * {@inheritDoc}
     */
    public function storeAccessToken($service, TokenInterface $token)
    {
        $serializedToken = serialize($token);

        if (isset($_SESSION[$this->sessionVariableName])
            && is_array($_SESSION[$this->sessionVariableName])
        ) {
            $_SESSION[$this->sessionVariableName][$service] = $serializedToken;
        } else {
            $_SESSION[$this->sessionVariableName] = array(
                $service => $serializedToken,
            );
        }

        // allow chaining
        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function hasAccessToken($service)
    {
        return isset($_SESSION[$this->sessionVariableName], $_SESSION[$this->sessionVariableName][$service]);
    }

    /**
     * {@inheritDoc}
     */
    public function clearToken($service)
    {
        if (array_key_exists($service, $_SESSION[$this->sessionVariableName])) {
            unset($_SESSION[$this->sessionVariableName][$service]);
        }

        // allow chaining
        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function clearAllTokens()
    {
        unset($_SESSION[$this->sessionVariableName]);

        // allow chaining
        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function storeAuthorizationState($service, $state)
    {
        if (isset($_SESSION[$this->stateVariableName])
            && is_array($_SESSION[$this->stateVariableName])
        ) {
            $_SESSION[$this->stateVariableName][$service] = $state;
        } else {
            $_SESSION[$this->stateVariableName] = array(
                $service => $state,
            );
        }

        // allow chaining
        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function hasAuthorizationState($service)
    {
        return isset($_SESSION[$this->stateVariableName], $_SESSION[$this->stateVariableName][$service]);
    }

    /**
     * {@inheritDoc}
     */
    public function retrieveAuthorizationState($service)
    {
        if ($this->hasAuthorizationState($service)) {
            return $_SESSION[$this->stateVariableName][$service];
        }

        throw new AuthorizationStateNotFoundException('State not found in session, are you sure you stored it?');
    }

    /**
     * {@inheritDoc}
     */
    public function clearAuthorizationState($service)
    {
        if (array_key_exists($service, $_SESSION[$this->stateVariableName])) {
            unset($_SESSION[$this->stateVariableName][$service]);
        }

        // allow chaining
        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function clearAllAuthorizationStates()
    {
        unset($_SESSION[$this->stateVariableName]);

        // allow chaining
        return $this;
    }

    public function __destruct()
    {
        if ($this->startSession) {
            session_write_close();
        }
    }

    /**
     * Determine if the session has started.
     * @url http://stackoverflow.com/a/18542272/1470961
     * @return bool
     */
    protected function sessionHasStarted()
    {
        // For more modern PHP versions we use a more reliable method.
        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
            return session_status() != PHP_SESSION_NONE;
        }

        // Below PHP 5.4 we should test for the current session ID.
        return session_id() !== '';
    }
}

API.auth.php:

require_once(_base_.'/api/lib/lib.api.php');

/**
 * Manage token authentication
 */
class Auth_API extends API {

    public function __construct() {
        //do not request auth code or won't log user at beginning
        parent::__construct();
        $this->needAuthentication = false;
    }

    /**
     * Get information about the authetication mode
     */
    final protected function getAuthenticationMethod() {
        $result = '';
        switch (Get::sett('rest_auth_method')) {
            case _AUTH_UCODE: { $result = _AUTH_UCODE_DESC; } break;
            case _AUTH_TOKEN: { $result = _AUTH_TOKEN_DESC; } break;
            case _AUTH_SECRET_KEY: { $result = _AUTH_SECRET_KEY_DESC; } break;
        }
        $mode = array( 'success'=>($result != ''), 'method'=>$result );
        return $mode;
    }

    /**
     * Log user and generate a token
     * @param <string> $username username of the user that we want to authenticate
     * @param <string> $password password of the user that we want to authenticate
     * @return <array> the auth token for the session
     */
    protected function generateToken($username, $password, $third_party =false) {

        $query = "SELECT * FROM %adm_user WHERE "
            ." userid = '".$this->aclManager->absoluteId($username)."'";
        $res = $this->db->query($query);

        $result = false;
        if($this->db->num_rows($res) > 0) {
            $row = $this->db->fetch_obj($res);
            if ($this->aclManager->password_verify_update($password,$row->pass,$row->idst)){
                if($third_party != false) {

                    $query = "SELECT * FROM %adm_user WHERE "
                        ." userid = '".$this->aclManager->absoluteId($third_party)."'";
                    $res = $this->db->query($query);

                }



                $timenow = time();
                $now = date("Y-m-d H:i:s", $timenow);
                $level = $this->aclManager->getUserLevelId($row->idst);
                $token = md5(uniqid(rand(), true) + $username);

                $lifetime = Get::sett('rest_auth_lifetime', 1);
                $expire = date("Y-m-d H:i:s", $timenow + $lifetime) ;

                // check if the user is already authenticate
                $query = "SELECT * FROM %adm_rest_authentication WHERE id_user = ".$row->idst;
                $res = $this->db->query($query);
                if ($this->db->num_rows($res) > 0) {

                    // update log table, if so, than re-authenticate it
                    $query = "UPDATE %adm_rest_authentication ".
                        " SET token='$token', generation_date='".$now."', last_enter_date=NULL, expiry_date='".$expire."' ".
                        " WHERE id_user=".$row->idst;
                    $res = $this->db->query($query);
                } else {

                    // set authentication in DB
                    $query = "INSERT INTO %adm_rest_authentication ".
                        "(id_user,user_level, token, generation_date, last_enter_date, expiry_date) VALUES ".
                        "('".$row->idst."', '".$level."', '".$token."', '".$now."', NULL, '".$expire."')";
                    $res = $this->db->query($query);
                }
                $result = array('success'=>true, 'token'=>$token, 'expire_at'=>$expire);
            } else {
                $result = array('success'=>false, 'message'=>'Error: invalid auth.');
            }

        } else {
            $result = array('success'=>false, 'message'=>'Error: invalid auth.');
        }
        return $result;
    }


    public function getauthmethod($params) {
        return $this->getAuthenticationMethod();
    }

    public function authenticate($params) {

        $auth_method = Get::sett('rest_auth_method', 'none');
        if($auth_method != _REST_AUTH_TOKEN && $auth_method != _REST_AUTH_CODE) {
            return array('success'=>false, 'message'=>'Error: Tokens are not used on this installation.');
        }

    //** BUG 2466 FIXED - SOAP  AUTHENTICATION ERROR WITH GENERATED TOKEN **
    if (isset($params['username']) && isset($params['password']) ) {
        $username = $params['username']; //params[0] should contain username
        $password = $params['password'];
        $third_party = $params['third_party'];
    }else{
        $username = Get::req('username', DOTY_STRING, false);
        $password = Get::req('password', DOTY_STRING, false);
        $third_party = Get::req('third_party', DOTY_STRING, false);
    }

        if ($username == false || $password === false) {
            //error: no login data provided
            return array('success'=>false, 'message'=>'Error: invalid login data provided.');
        } else {

            $res = $this->generateToken($username, $password, $third_party);
            if ($res['success']) {
                $output = array(
                    'success'=>true,
                    'message'=>'You are authenticated.',
                    'token'=>$res['token'],
                    'expire_at'=>$res['expire_at']
                );
            } else {
                $output = array('success'=>false, 'message'=>'Error: invalid user.');
            }
            return $output;
        }
    }
}

What I've done so far is adding a session_destroy function right before logout redirect. Will see if it solves the issue. So, what's causing this and how do I prevent this issue?

    if (Docebo::user()->isLoggedIn() && $login_user != Docebo::user()->getACLManager()->relativeId(Docebo::user()->userid)) {
            AuthenticationManager::logout();
-------->    session_destroy();
            header("Location: " . $_SERVER['REQUEST_URI']);
            exit;
        }

logout function

public function logout()
{

    $msg = Get::req("msg", DOTY_MIXED, null);

    if (Docebo::user()->isAnonymous()) self::redirect();

    AuthenticationManager::logout();

    $redirection = array();

    $redirection['req'] = _homepage_;
    if ($msg) {
        $redirection['query'] = array(
            "msg" => $msg
        );
    } else {
        $redirection['query'] = array(
            "done" => LOGGED_OUT
        );
    }
    self::redirect($redirection);
}
James Z
  • 12,209
  • 10
  • 24
  • 44
  • Are you saying you are still logged in as an Admin or that it just redirects you back to the page you came from after logging in? I'm focusing (didn't read most of the code) on the last line, `header("Location: " . $_SERVER['REQUEST_URI']);` – ficuscr Feb 22 '22 at 07:44
  • After I log in as a normal user, I become the admin somehow. I can see my admin profile and can do privileged tasks. Why are you focused n that particular line? – Marcus Rudolph Feb 22 '22 at 07:54
  • Not seeing the `logout` function. "In order to kill the session altogether, the session ID must also be unset. If a cookie is used to propagate the session ID (default behavior), then the session cookie must be deleted. setcookie() may be used for that." – ficuscr Feb 22 '22 at 08:00
  • @ficuscr I added the logout function in OP. The session argument is just a guess. Don't know if it's the fact the root of this issue. Will try out the suggested post and will report back in case someone who is also experiencing this. Thanks – Marcus Rudolph Feb 22 '22 at 08:42

0 Answers0