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