From e8023c308f06dd0c0ffdafb0dfe30cc9ac450d8e Mon Sep 17 00:00:00 2001 From: Diego Najar Date: Tue, 7 Nov 2017 00:18:16 +0100 Subject: [PATCH] Remember me --- bl-kernel/admin/controllers/login.php | 36 +++++++++++- bl-kernel/boot/init.php | 5 ++ bl-kernel/dbusers.class.php | 39 ++++++++----- bl-kernel/helpers/cookie.class.php | 26 +++++---- bl-kernel/login.class.php | 84 +++++++++++++-------------- bl-themes/kernel-panic/css/bludit.css | 2 +- 6 files changed, 120 insertions(+), 72 deletions(-) diff --git a/bl-kernel/admin/controllers/login.php b/bl-kernel/admin/controllers/login.php index 63f18990..a4e0c05c 100644 --- a/bl-kernel/admin/controllers/login.php +++ b/bl-kernel/admin/controllers/login.php @@ -20,6 +20,9 @@ function checkLogin($args) } if ($Login->verifyUser($_POST['username'], $_POST['password'])) { + if (isset($_POST['remember'])) { + $Login->setRememberMe($_POST['username']); + } // Renew the token. This token will be the same inside the session for multiple forms. $Security->generateTokenCSRF(); Redirect::page('dashboard'); @@ -35,16 +38,45 @@ function checkLogin($args) return false; } +function checkRememberMe() +{ + global $Security; + global $Login; + + if ($Security->isBlocked()) { + return false; + } + + if (!Cookie::isset(REMEMBER_COOKIE_USERNAME) || !Cookie::isset(REMEMBER_COOKIE_TOKEN)) { + return false; + } + + $username = Cookie::get(REMEMBER_COOKIE_USERNAME); + $token = Cookie::get(REMEMBER_COOKIE_TOKEN); + + if ($Login->verifyUserByRemember($username, $token)) { + $Security->generateTokenCSRF(); + Redirect::page('dashboard'); + return true; + } + + $Security->addToBlacklist(); + return false; +} + // ============================================================================ // Main before POST // ============================================================================ +if ($_SERVER['REQUEST_METHOD']!=='POST') { + checkRememberMe(); +} + // ============================================================================ // POST Method // ============================================================================ -if ($_SERVER['REQUEST_METHOD']=='POST') -{ +if ($_SERVER['REQUEST_METHOD']=='POST') { checkLogin($_POST); } diff --git a/bl-kernel/boot/init.php b/bl-kernel/boot/init.php index e798d5e2..8a8aaf29 100644 --- a/bl-kernel/boot/init.php +++ b/bl-kernel/boot/init.php @@ -110,6 +110,11 @@ define('CLI_STATUS', 'published'); // Cli mode, username for new pages define('CLI_USERNAME', 'admin'); +// Remember me +define('REMEMBER_COOKIE_USERNAME', 'BLUDITREMEMBERUSERNAME'); +define('REMEMBER_COOKIE_TOKEN', 'BLUDITREMEMBERTOKEN'); +define('REMEMBER_COOKIE_EXPIRE_IN_DAYS', 30); + // Filename define('FILENAME', 'index.txt'); diff --git a/bl-kernel/dbusers.class.php b/bl-kernel/dbusers.class.php index ad264295..c432c51b 100644 --- a/bl-kernel/dbusers.class.php +++ b/bl-kernel/dbusers.class.php @@ -11,8 +11,7 @@ class dbUsers extends dbJSON 'salt'=> array('inFile'=>false, 'value'=>'!Pink Floyd!Welcome to the machine!'), 'email'=> array('inFile'=>false, 'value'=>''), 'registered'=> array('inFile'=>false, 'value'=>'1985-03-15 10:00'), - 'tokenEmail'=> array('inFile'=>false, 'value'=>''), - 'tokenEmailTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'), + 'tokenRemember'=> array('inFile'=>false, 'value'=>''), 'tokenAuth'=> array('inFile'=>false, 'value'=>''), 'tokenAuthTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'), 'twitter'=> array('inFile'=>false, 'value'=>''), @@ -119,7 +118,7 @@ class dbUsers extends dbJSON return md5( uniqid().time().DOMAIN ); } - public function generateEmailToken() + public function generateRememberToken() { return $this->generateAuthToken(); } @@ -134,6 +133,13 @@ class dbUsers extends dbJSON return sha1($password.$salt); } + public function setRememberToken($username, $token) + { + $args['username'] = $username; + $args['tokenRemember'] = $token; + return $this->set($args); + } + public function setPassword($username, $password) { $salt = $this->generateSalt(); @@ -170,18 +176,25 @@ class dbUsers extends dbJSON return false; } - public function setTokenEmail($username) + // Returns the username with the remember token assigned, FALSE otherwise + public function getByRememberToken($token) { - // Random hash - $token = $this->generateEmailToken(); - $this->db[$username]['tokenEmail'] = $token; + foreach ($this->db as $username=>$fields) { + if ($fields['tokenRemember']==$token) { + return $username; + } + } + return false; + } - // Token time to live, defined by TOKEN_EMAIL_TTL - $this->db[$username]['tokenEmailTTL'] = Date::currentOffset(DB_DATE_FORMAT, TOKEN_EMAIL_TTL); - - // Save the database - $this->save(); - return $token; + // This function clean all tokens for Remember me + // This function is used when some hacker try to use an invalid remember token + public function invalidateAllRememberTokens() + { + foreach ($this->db as $username=>$values) { + $this->db[$username]['tokenRemember'] = ''; + } + return $this->save(); } // Returns array with the username databases filtered by username, FALSE otherwise diff --git a/bl-kernel/helpers/cookie.class.php b/bl-kernel/helpers/cookie.class.php index 784ce602..9c120ffb 100644 --- a/bl-kernel/helpers/cookie.class.php +++ b/bl-kernel/helpers/cookie.class.php @@ -2,24 +2,28 @@ class Cookie { - public static function get($name) + public static function get($key) { - if(isset($_COOKIE[$name])) - { - return($_COOKIE[$name]); + if (isset($_COOKIE[$key])) { + return $_COOKIE[$name]; } - - return(false); + return false; } - public static function add($name, $value, $expire = 525600) + public static function set($key, $value, $daysToExpire=30) { - setcookie($name, $value, time() + ($expire * 60)); + // The time the cookie expires. + // This is a Unix timestamp so is in number of seconds since the epoch. + // In other words, you'll most likely set this with the time() function plus the number of seconds before you want it to expire. + // Or you might use mktime(). time()+60*60*24*30 will set the cookie to expire in 30 days. + // If set to 0, or omitted, the cookie will expire at the end of the session (when the browser closes). + $expire = time()+60*60*24*$daysToExpire; + setcookie($key, $value, $expire); } - public static function isSet($name) + public static function isset($key) { - return(isset($_COOKIE[$name])); + return isset($_COOKIE[$key]); } -} +} \ No newline at end of file diff --git a/bl-kernel/login.class.php b/bl-kernel/login.class.php index 96d5e1e1..c48a2ee1 100644 --- a/bl-kernel/login.class.php +++ b/bl-kernel/login.class.php @@ -9,11 +9,13 @@ class Login { $this->dbUsers = $dbUsers; } + // Returns the username of the user logged public function username() { return Session::get('username'); } + // Returns the role of the user logged public function role() { return Session::get('role'); @@ -26,9 +28,8 @@ class Login { $username = Session::get('username'); if (!empty($username)) { return true; - } - else { - Log::set(__METHOD__.LOG_SEP.'Session username empty, destroy the session.'); + } else { + Log::set(__METHOD__.LOG_SEP.'Session username empty, destroying the session.'); Session::destroy(); return false; } @@ -49,6 +50,19 @@ class Login { Log::set(__METHOD__.LOG_SEP.'User logged, fingerprint: '.$this->fingerPrint()); } + public function setRememberMe($username) + { + $username = Sanitize::html($username); + + // Set the token on the users database + $token = $this->dbUsers->generateRememberToken(); + $this->dbUsers->setRememberToken($username, $token); + + // Set the token on the cookies + Cookie::set(REMEMBER_COOKIE_USERNAME, $username, REMEMBER_COOKIE_EXPIRE_IN_DAYS); + Cookie::set(REMEMBER_COOKIE_TOKEN, $token, REMEMBER_COOKIE_EXPIRE_IN_DAYS); + } + // Check if the username and the password are valid // Returns TRUE if valid and set the session // Returns FALSE for invalid username or password @@ -71,7 +85,7 @@ class Login { } $user = $this->dbUsers->getDB($username); - if($user==false) { + if ($user==false) { Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username); return false; } @@ -82,65 +96,46 @@ class Login { Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username); return true; } - else { - Log::set(__METHOD__.LOG_SEP.'Password incorrect.'); - } + Log::set(__METHOD__.LOG_SEP.'Password incorrect.'); return false; } - public function verifyUserByToken($username, $token) + // Verified Remember Token + // If valid log in the user + // If not valid invalidate all remember me tokens + public function verifyUserByRemember($username, $token) { - $username = Sanitize::html($username); - $token = Sanitize::html($token); + $username = Sanitize::html($username); + $token = Sanitize::html($token); - $username = trim($username); - $token = trim($token); + $username = trim($username); + $token = trim($token); - if(empty($username) || empty($token)) { - Log::set(__METHOD__.LOG_SEP.'Username or Token-email empty. Username: '.$username.' - Token-email: '.$token); + if (empty($username) || empty($token)) { + $this->dbUsers->invalidateAllRememberTokens(); + Log::set(__METHOD__.LOG_SEP.'Username or Token empty. Username: '.$username.' - Token: '.$token); return false; } + if ($username !== $this->getByRememberToken($token)) { + $this->dbUsers->invalidateAllRememberTokens(); + Log::set(__METHOD__.LOG_SEP.'The user has different token or the token doesnt exist.'); + return false; + } + + // Validate user and login $user = $this->dbUsers->getDb($username); - if($user==false) { - Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username); - return false; - } - - $currentTime = Date::current(DB_DATE_FORMAT); - if($user['tokenEmailTTL']<$currentTime) { - Log::set(__METHOD__.LOG_SEP.'Token-email expired: '.$username); - return false; - } - - if($token === $user['tokenEmail']) - { - // Set the user loggued. - $this->setLogin($username, $user['role']); - - // Invalidate the current token. - $this->dbUsers->setTokenEmail($username); - - Log::set(__METHOD__.LOG_SEP.'User logged succeeded by Token-email - Username: '.$username); - - return true; - } - else { - Log::set(__METHOD__.LOG_SEP.'Token-email incorrect.'); - } - - return false; + $this->setLogin($username, $user['role']); + return true; } public function fingerPrint() { - // User agent $agent = getenv('HTTP_USER_AGENT'); if (empty($agent)) { $agent = 'Bludit/2.0 (Mr Nibbler Protocol)'; } - return sha1($agent); } @@ -148,5 +143,4 @@ class Login { { return Session::destroy(); } - } \ No newline at end of file diff --git a/bl-themes/kernel-panic/css/bludit.css b/bl-themes/kernel-panic/css/bludit.css index c4c04b66..ee005e89 100755 --- a/bl-themes/kernel-panic/css/bludit.css +++ b/bl-themes/kernel-panic/css/bludit.css @@ -37,7 +37,7 @@ h2 { } /* PAGES */ -article.page:not(:last-child) { +article.page { margin-bottom: 100px; }