Remember me

This commit is contained in:
Diego Najar 2017-11-07 00:18:16 +01:00
parent eb93088f87
commit e8023c308f
6 changed files with 120 additions and 72 deletions

View File

@ -20,6 +20,9 @@ function checkLogin($args)
} }
if ($Login->verifyUser($_POST['username'], $_POST['password'])) { 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. // Renew the token. This token will be the same inside the session for multiple forms.
$Security->generateTokenCSRF(); $Security->generateTokenCSRF();
Redirect::page('dashboard'); Redirect::page('dashboard');
@ -35,16 +38,45 @@ function checkLogin($args)
return false; 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 // Main before POST
// ============================================================================ // ============================================================================
if ($_SERVER['REQUEST_METHOD']!=='POST') {
checkRememberMe();
}
// ============================================================================ // ============================================================================
// POST Method // POST Method
// ============================================================================ // ============================================================================
if ($_SERVER['REQUEST_METHOD']=='POST') if ($_SERVER['REQUEST_METHOD']=='POST') {
{
checkLogin($_POST); checkLogin($_POST);
} }

View File

@ -110,6 +110,11 @@ define('CLI_STATUS', 'published');
// Cli mode, username for new pages // Cli mode, username for new pages
define('CLI_USERNAME', 'admin'); define('CLI_USERNAME', 'admin');
// Remember me
define('REMEMBER_COOKIE_USERNAME', 'BLUDITREMEMBERUSERNAME');
define('REMEMBER_COOKIE_TOKEN', 'BLUDITREMEMBERTOKEN');
define('REMEMBER_COOKIE_EXPIRE_IN_DAYS', 30);
// Filename // Filename
define('FILENAME', 'index.txt'); define('FILENAME', 'index.txt');

View File

@ -11,8 +11,7 @@ class dbUsers extends dbJSON
'salt'=> array('inFile'=>false, 'value'=>'!Pink Floyd!Welcome to the machine!'), 'salt'=> array('inFile'=>false, 'value'=>'!Pink Floyd!Welcome to the machine!'),
'email'=> array('inFile'=>false, 'value'=>''), 'email'=> array('inFile'=>false, 'value'=>''),
'registered'=> array('inFile'=>false, 'value'=>'1985-03-15 10:00'), 'registered'=> array('inFile'=>false, 'value'=>'1985-03-15 10:00'),
'tokenEmail'=> array('inFile'=>false, 'value'=>''), 'tokenRemember'=> array('inFile'=>false, 'value'=>''),
'tokenEmailTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'),
'tokenAuth'=> array('inFile'=>false, 'value'=>''), 'tokenAuth'=> array('inFile'=>false, 'value'=>''),
'tokenAuthTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'), 'tokenAuthTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'),
'twitter'=> array('inFile'=>false, 'value'=>''), 'twitter'=> array('inFile'=>false, 'value'=>''),
@ -119,7 +118,7 @@ class dbUsers extends dbJSON
return md5( uniqid().time().DOMAIN ); return md5( uniqid().time().DOMAIN );
} }
public function generateEmailToken() public function generateRememberToken()
{ {
return $this->generateAuthToken(); return $this->generateAuthToken();
} }
@ -134,6 +133,13 @@ class dbUsers extends dbJSON
return sha1($password.$salt); return sha1($password.$salt);
} }
public function setRememberToken($username, $token)
{
$args['username'] = $username;
$args['tokenRemember'] = $token;
return $this->set($args);
}
public function setPassword($username, $password) public function setPassword($username, $password)
{ {
$salt = $this->generateSalt(); $salt = $this->generateSalt();
@ -170,18 +176,25 @@ class dbUsers extends dbJSON
return false; return false;
} }
public function setTokenEmail($username) // Returns the username with the remember token assigned, FALSE otherwise
public function getByRememberToken($token)
{ {
// Random hash foreach ($this->db as $username=>$fields) {
$token = $this->generateEmailToken(); if ($fields['tokenRemember']==$token) {
$this->db[$username]['tokenEmail'] = $token; return $username;
}
}
return false;
}
// Token time to live, defined by TOKEN_EMAIL_TTL // This function clean all tokens for Remember me
$this->db[$username]['tokenEmailTTL'] = Date::currentOffset(DB_DATE_FORMAT, TOKEN_EMAIL_TTL); // This function is used when some hacker try to use an invalid remember token
public function invalidateAllRememberTokens()
// Save the database {
$this->save(); foreach ($this->db as $username=>$values) {
return $token; $this->db[$username]['tokenRemember'] = '';
}
return $this->save();
} }
// Returns array with the username databases filtered by username, FALSE otherwise // Returns array with the username databases filtered by username, FALSE otherwise

View File

@ -2,24 +2,28 @@
class Cookie { class Cookie {
public static function get($name) public static function get($key)
{ {
if(isset($_COOKIE[$name])) if (isset($_COOKIE[$key])) {
{ return $_COOKIE[$name];
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]);
} }
} }

View File

@ -9,11 +9,13 @@ class Login {
$this->dbUsers = $dbUsers; $this->dbUsers = $dbUsers;
} }
// Returns the username of the user logged
public function username() public function username()
{ {
return Session::get('username'); return Session::get('username');
} }
// Returns the role of the user logged
public function role() public function role()
{ {
return Session::get('role'); return Session::get('role');
@ -26,9 +28,8 @@ class Login {
$username = Session::get('username'); $username = Session::get('username');
if (!empty($username)) { if (!empty($username)) {
return true; return true;
} } else {
else { Log::set(__METHOD__.LOG_SEP.'Session username empty, destroying the session.');
Log::set(__METHOD__.LOG_SEP.'Session username empty, destroy the session.');
Session::destroy(); Session::destroy();
return false; return false;
} }
@ -49,6 +50,19 @@ class Login {
Log::set(__METHOD__.LOG_SEP.'User logged, fingerprint: '.$this->fingerPrint()); 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 // Check if the username and the password are valid
// Returns TRUE if valid and set the session // Returns TRUE if valid and set the session
// Returns FALSE for invalid username or password // Returns FALSE for invalid username or password
@ -71,7 +85,7 @@ class Login {
} }
$user = $this->dbUsers->getDB($username); $user = $this->dbUsers->getDB($username);
if($user==false) { if ($user==false) {
Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username); Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username);
return false; return false;
} }
@ -82,65 +96,46 @@ class Login {
Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username); Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username);
return true; return true;
} }
else {
Log::set(__METHOD__.LOG_SEP.'Password incorrect.');
}
Log::set(__METHOD__.LOG_SEP.'Password incorrect.');
return false; 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); $username = Sanitize::html($username);
$token = Sanitize::html($token); $token = Sanitize::html($token);
$username = trim($username); $username = trim($username);
$token = trim($token); $token = trim($token);
if(empty($username) || empty($token)) { if (empty($username) || empty($token)) {
Log::set(__METHOD__.LOG_SEP.'Username or Token-email empty. Username: '.$username.' - Token-email: '.$token); $this->dbUsers->invalidateAllRememberTokens();
Log::set(__METHOD__.LOG_SEP.'Username or Token empty. Username: '.$username.' - Token: '.$token);
return false; 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); $user = $this->dbUsers->getDb($username);
if($user==false) { $this->setLogin($username, $user['role']);
Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username); return true;
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;
} }
public function fingerPrint() public function fingerPrint()
{ {
// User agent
$agent = getenv('HTTP_USER_AGENT'); $agent = getenv('HTTP_USER_AGENT');
if (empty($agent)) { if (empty($agent)) {
$agent = 'Bludit/2.0 (Mr Nibbler Protocol)'; $agent = 'Bludit/2.0 (Mr Nibbler Protocol)';
} }
return sha1($agent); return sha1($agent);
} }
@ -148,5 +143,4 @@ class Login {
{ {
return Session::destroy(); return Session::destroy();
} }
} }

View File

@ -37,7 +37,7 @@ h2 {
} }
/* PAGES */ /* PAGES */
article.page:not(:last-child) { article.page {
margin-bottom: 100px; margin-bottom: 100px;
} }