From 7f8e0124860c0e1fb6fa91d8e27057ebb7517f49 Mon Sep 17 00:00:00 2001 From: Diego Najar Date: Sun, 16 Jul 2017 00:42:37 +0200 Subject: [PATCH] Login and Security improves --- bl-kernel/admin/controllers/login-email.php | 2 +- bl-kernel/admin/controllers/login.php | 15 ++-- bl-kernel/admin/themes/default/index.php | 3 + bl-kernel/admin/views/login-email.php | 2 +- bl-kernel/admin/views/login.php | 2 +- bl-kernel/boot/admin.php | 6 +- bl-kernel/boot/init.php | 1 + bl-kernel/dbusers.class.php | 13 ++-- bl-kernel/helpers/session.class.php | 4 - bl-kernel/login.class.php | 77 ++++++++------------ bl-kernel/security.class.php | 75 ++++++------------- bl-themes/kernel-panic/favicon.png | Bin 0 -> 1025 bytes install.php | 3 - 13 files changed, 76 insertions(+), 127 deletions(-) create mode 100644 bl-themes/kernel-panic/favicon.png diff --git a/bl-kernel/admin/controllers/login-email.php b/bl-kernel/admin/controllers/login-email.php index 0c8101dd..685f7a60 100644 --- a/bl-kernel/admin/controllers/login-email.php +++ b/bl-kernel/admin/controllers/login-email.php @@ -91,7 +91,7 @@ function checkGet($args) } // Bruteforce protection, add IP to blacklist. - $Security->addLoginFail(); + $Security->addToBlacklist(); return false; } diff --git a/bl-kernel/admin/controllers/login.php b/bl-kernel/admin/controllers/login.php index 7a3b8b22..63f18990 100644 --- a/bl-kernel/admin/controllers/login.php +++ b/bl-kernel/admin/controllers/login.php @@ -8,27 +8,26 @@ // Functions // ============================================================================ -function checkPost($args) +function checkLogin($args) { global $Security; global $Login; global $Language; - if($Security->isBlocked()) { + if ($Security->isBlocked()) { Alert::set($Language->g('IP address has been blocked').'
'.$Language->g('Try again in a few minutes')); return false; } - // Verify User sanitize the input - if( $Login->verifyUser($_POST['username'], $_POST['password']) ) { + if ($Login->verifyUser($_POST['username'], $_POST['password'])) { // Renew the token. This token will be the same inside the session for multiple forms. $Security->generateTokenCSRF(); Redirect::page('dashboard'); return true; } - // Bruteforce protection, add IP to blacklist. - $Security->addLoginFail(); + // Bruteforce protection, add IP to the blacklist + $Security->addToBlacklist(); // Create alert Alert::set($Language->g('Username or password incorrect')); @@ -44,9 +43,9 @@ function checkPost($args) // POST Method // ============================================================================ -if( $_SERVER['REQUEST_METHOD'] == 'POST' ) +if ($_SERVER['REQUEST_METHOD']=='POST') { - checkPost($_POST); + checkLogin($_POST); } // ============================================================================ diff --git a/bl-kernel/admin/themes/default/index.php b/bl-kernel/admin/themes/default/index.php index ddb6b3d9..a3fc509e 100644 --- a/bl-kernel/admin/themes/default/index.php +++ b/bl-kernel/admin/themes/default/index.php @@ -144,6 +144,9 @@ $(document).ready(function() { Hey!'; + echo '

Have you seen my ball?

'; } ?> diff --git a/bl-kernel/admin/views/login-email.php b/bl-kernel/admin/views/login-email.php index cc8dd39f..ab0f412e 100644 --- a/bl-kernel/admin/views/login-email.php +++ b/bl-kernel/admin/views/login-email.php @@ -4,7 +4,7 @@
- +
diff --git a/bl-kernel/admin/views/login.php b/bl-kernel/admin/views/login.php index 2330e08a..bb2ece3e 100644 --- a/bl-kernel/admin/views/login.php +++ b/bl-kernel/admin/views/login.php @@ -2,7 +2,7 @@ - +
diff --git a/bl-kernel/boot/admin.php b/bl-kernel/boot/admin.php index dbc5ffd1..8e53517c 100644 --- a/bl-kernel/boot/admin.php +++ b/bl-kernel/boot/admin.php @@ -50,14 +50,12 @@ else // User not logged. // Slug is login. // Slug is login-email. - if($Url->notFound() || !$Login->isLogged() || ($Url->slug()==='login') || ($Url->slug()==='login-email') ) - { + if($Url->notFound() || !$Login->isLogged() || ($Url->slug()==='login') || ($Url->slug()==='login-email') ) { $layout['controller'] = 'login'; $layout['view'] = 'login'; $layout['template'] = 'login.php'; - if($Url->slug()==='login-email') - { + if ($Url->slug()==='login-email') { $layout['controller'] = 'login-email'; $layout['view'] = 'login-email'; } diff --git a/bl-kernel/boot/init.php b/bl-kernel/boot/init.php index 02b06f15..a13c9f53 100644 --- a/bl-kernel/boot/init.php +++ b/bl-kernel/boot/init.php @@ -57,6 +57,7 @@ define('DB_CATEGORIES', PATH_DATABASES.'categories.php'); define('DB_TAGS', PATH_DATABASES.'tags.php'); define('DB_SYSLOG', PATH_DATABASES.'syslog.php'); define('DB_USERS', PATH_DATABASES.'users.php'); +define('DB_SECURITY', PATH_DATABASES.'security.php'); // Log separator define('LOG_SEP', ' | '); diff --git a/bl-kernel/dbusers.class.php b/bl-kernel/dbusers.class.php index b1c9699f..15c2d87f 100644 --- a/bl-kernel/dbusers.class.php +++ b/bl-kernel/dbusers.class.php @@ -185,19 +185,18 @@ class dbUsers extends dbJSON return $token; } -// ---- OLD // Returns array with the username databases filtered by username, FALSE otherwise - public function getDb($username) + public function getDB($username) { - if($this->exists($username)) { - $user = $this->db[$username]; - - return $user; + if ($this->exists($username)) { + return $this->db[$username]; } - return false; } +// ---- OLD + + public function getAll() { return $this->db; diff --git a/bl-kernel/helpers/session.class.php b/bl-kernel/helpers/session.class.php index d2f19246..ddc8cff4 100644 --- a/bl-kernel/helpers/session.class.php +++ b/bl-kernel/helpers/session.class.php @@ -55,13 +55,9 @@ public static function destroy() { session_destroy(); - unset($_SESSION); - self::$started = false; - Log::set(__METHOD__.LOG_SEP.'Session destroyed.'); - return !isset($_SESSION); } diff --git a/bl-kernel/login.class.php b/bl-kernel/login.class.php index 12136dcc..b0d4f06e 100644 --- a/bl-kernel/login.class.php +++ b/bl-kernel/login.class.php @@ -19,6 +19,26 @@ class Login { return Session::get('role'); } + // Returns TRUE if the user is logged, FALSE otherwise + public function isLogged() + { + if (Session::get('fingerPrint')===$this->fingerPrint()) { + $username = Session::get('username'); + if (!empty($username)) { + return true; + } + else { + Log::set(__METHOD__.LOG_SEP.'Session username empty, destroy the session.'); + Session::destroy(); + return false; + } + } + + Log::set(__METHOD__.LOG_SEP.'FingerPrint are differents. Current fingerPrint: '.Session::get('fingerPrint').' !== Current fingerPrint: '.$this->fingerPrint()); + return false; + } + + // Set the session for the user logged public function setLogin($username, $role) { Session::set('username', $username); @@ -26,30 +46,12 @@ class Login { Session::set('fingerPrint', $this->fingerPrint()); Session::set('sessionTime', time()); - Log::set(__METHOD__.LOG_SEP.'Set fingerPrint: '.$this->fingerPrint()); - } - - public function isLogged() - { - if(Session::get('fingerPrint')===$this->fingerPrint()) - { - $username = Session::get('username'); - - if(!empty($username)) { - return true; - } - else { - Log::set(__METHOD__.LOG_SEP.'Session username empty: '.$username); - } - } - else - { - Log::set(__METHOD__.LOG_SEP.'FingerPrint are differents. Session fingerPrint: '.Session::get('fingerPrint').' !== Current fingerPrint: '.$this->fingerPrint()); - } - - return false; + Log::set(__METHOD__.LOG_SEP.'User logged, fingerprint: '.$this->fingerPrint()); } + // Check if the username and the password are valid + // Returns TRUE if valid and set the session + // Returns FALSE for invalid username or password public function verifyUser($username, $password) { $username = Sanitize::html($username); @@ -58,25 +60,21 @@ class Login { $username = trim($username); $password = trim($password); - if(empty($username) || empty($password)) { + if (empty($username) || empty($password)) { Log::set(__METHOD__.LOG_SEP.'Username or password empty. Username: '.$username.' - Password: '.$password); return false; } - $user = $this->dbUsers->getDb($username); + $user = $this->dbUsers->getDB($username); if($user==false) { Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username); return false; } - $passwordHash = sha1($password.$user['salt']); - - if($passwordHash === $user['password']) - { + $passwordHash = $this->dbUsers->generatePasswordHash($password, $user['salt']); + if ($passwordHash===$user['password']) { $this->setLogin($username, $user['role']); - Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username); - return true; } else { @@ -130,27 +128,14 @@ class Login { return false; } - public function fingerPrint($random=false) + public function fingerPrint() { // User agent $agent = getenv('HTTP_USER_AGENT'); - if(empty($agent)) { - $agent = 'Bludit/1.0 (Mr Nibbler Protocol)'; + if (empty($agent)) { + $agent = 'Bludit/2.0 (Mr Nibbler Protocol)'; } - // User IP - if(getenv('HTTP_X_FORWARDED_FOR')) - $ip = getenv('HTTP_X_FORWARDED_FOR'); - elseif(getenv('HTTP_CLIENT_IP')) - $ip = getenv('HTTP_CLIENT_IP'); - else - $ip = getenv('REMOTE_ADDR'); - - if($random) { - return sha1(mt_rand().$agent.$ip); - } - - // DEBUG: Ver CLIENT IP, hay veces que retorna la ip ::1 y otras 127.0.0.1 return sha1($agent); } diff --git a/bl-kernel/security.class.php b/bl-kernel/security.class.php index 15befe9b..c2e1b9b7 100644 --- a/bl-kernel/security.class.php +++ b/bl-kernel/security.class.php @@ -1,9 +1,8 @@ -'Where we go we dont need roads', 'minutesBlocked'=>5, 'numberFailuresAllowed'=>10, 'blackList'=>array() @@ -11,51 +10,33 @@ class Security extends dbJSON function __construct() { - parent::__construct(PATH_DATABASES.'security.php'); + parent::__construct(DB_SECURITY); } - // Authentication key - // -------------------------------------------------------------------- - public function key1() - { - return $this->db['key1']; - } - - // ==================================================== // TOKEN FOR CSRF // ==================================================== - // Generate and save the token in Session. + // Generate and save the token in Session public function generateTokenCSRF() { - $token = Text::randomText(8); - $token = sha1($token); - - Log::set(__METHOD__.LOG_SEP.'New tokenCSRF was generated '.$token); - + $token = sha1( uniqid().time() ); Session::set('tokenCSRF', $token); } - // Validate the token. + // Validate the token public function validateTokenCSRF($token) { - $sessionToken = Session::get('tokenCSRF'); - + $sessionToken = $this->getTokenCSRF(); return ( !empty($sessionToken) && ($sessionToken===$token) ); } - // Returns the token. + // Returns the token public function getTokenCSRF() { return Session::get('tokenCSRF'); } - public function printTokenCSRF() - { - echo Session::get('tokenCSRF'); - } - // ==================================================== // BRUTE FORCE PROTECTION // ==================================================== @@ -64,7 +45,7 @@ class Security extends dbJSON { $ip = $this->getUserIp(); - if(!isset($this->db['blackList'][$ip])) { + if (!isset($this->db['blackList'][$ip])) { return false; } @@ -73,51 +54,42 @@ class Security extends dbJSON $numberFailures = $userBlack['numberFailures']; $lastFailure = $userBlack['lastFailure']; - // Check if the IP is expired, then is not blocked. - if($currentTime > $lastFailure + ($this->db['minutesBlocked']*60)) { + // Check if the IP is expired, then is not blocked + if ($currentTime > $lastFailure + ($this->db['minutesBlocked']*60)) { return false; } - // The IP has more failures than number of failures, then the IP is blocked. - if($numberFailures >= $this->db['numberFailuresAllowed']) { + // The IP has more failures than number of failures, then the IP is blocked + if ($numberFailures >= $this->db['numberFailuresAllowed']) { Log::set(__METHOD__.LOG_SEP.'IP Blocked:'.$ip); return true; } - // Otherwise the IP is not blocked. + // Otherwise the IP is not blocked return false; } - public function addLoginFail() + // Add or update the current client IP on the blacklist + public function addToBlacklist() { $ip = $this->getUserIp(); $currentTime = time(); $numberFailures = 1; - if(isset($this->db['blackList'][$ip])) - { + if (isset($this->db['blackList'][$ip])) { $userBlack = $this->db['blackList'][$ip]; $lastFailure = $userBlack['lastFailure']; - // Check if the IP is expired, then renew the number of failures. - if($currentTime <= $lastFailure + ($this->db['minutesBlocked']*60)) - { + // Check if the IP is expired, then renew the number of failures + if($currentTime <= $lastFailure + ($this->db['minutesBlocked']*60)) { $numberFailures = $userBlack['numberFailures']; $numberFailures = $numberFailures + 1; } } $this->db['blackList'][$ip] = array('lastFailure'=>$currentTime, 'numberFailures'=>$numberFailures); - Log::set(__METHOD__.LOG_SEP.'Blacklist, IP:'.$ip.', Number of failures:'.$numberFailures); - - // Save the database - if( $this->save() === false ) { - Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.'); - return false; - } - - return true; + return $this->save(); } public function getNumberFailures($ip=null) @@ -134,14 +106,13 @@ class Security extends dbJSON public function getUserIp() { - // User IP - if(getenv('HTTP_X_FORWARDED_FOR')) + if (getenv('HTTP_X_FORWARDED_FOR')) { $ip = getenv('HTTP_X_FORWARDED_FOR'); - elseif(getenv('HTTP_CLIENT_IP')) + } elseif (getenv('HTTP_CLIENT_IP')) { $ip = getenv('HTTP_CLIENT_IP'); - else + } else { $ip = getenv('REMOTE_ADDR'); - + } return $ip; } } diff --git a/bl-themes/kernel-panic/favicon.png b/bl-themes/kernel-panic/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..60349a2b41b19b00ec222b8e0e1994cb8ebd7d3f GIT binary patch literal 1025 zcmV+c1pfPpP)2CR?pZ05dPVI7AJ_cwKcwze@ZqB zldgHa-j=TG-vN-u0)V91Gj;&@z~RNk1)|X?8jS`P78dx9Jpe3X6{ScdqUpNct=H>5 zLy~Fd>;O=58S<~f!yaX38`QKm!ow|C*+Si^U?2j*if5HWhWIof`#!;|yw= zCX@+U2kPtqqygkAl?s-Zm*r)of-4IKgQ!-k@^Y>)91hDp{*tP6IxTq&{%&p2M9pW% z4gjlG)DSu@+aiJTJw87E+3j{enkm6ihM`dC^WEK@cT@m@K%m!ZwZ4v`0@f57Zf|d; zy=-TpP!I}IsnmcPD{!P~Wo3o8zW_LLD?{ds6gdavnm#}!@S2*MLakO4V3vA(89)xf z-=TODP81a9=H_A#4-dY@#YNoQ+(5~aAY!o?&d<*+0TA@;Eafjw$Zom=;Pd&~J3Bkm z)N|hhkjv#{)&0pH0OIs-Y-~t@>+9=5A*$o@^769p74854=qVLUOiZBD>EQ8Ec4xIR zD5vCnY;SK%j#0XlAyshHV)&I8SBlp*9p#hICz_|wzVNLI{l z&Ck#4tyb&Xs8%da$W>4cZ-Y~Rc6KJVyt&DP&r7hlrfJ<;t;R!%P@O98L82!VmmBM# v+ARRpaRQ-4d2XZ~vKW1y%m2r2?F{S>=?JMX!0RGF00000NkvXXu0mjfYZ>#^ literal 0 HcmV?d00001 diff --git a/install.php b/install.php index dba61526..5863aaac 100644 --- a/install.php +++ b/install.php @@ -418,10 +418,7 @@ function install($adminPassword, $email, $timezone) file_put_contents(PATH_DATABASES.'syslog.php', $dataHead.json_encode($data, JSON_PRETTY_PRINT), LOCK_EX); // File security.php - $randomKey = sha1( uniqid() ); - $data = array( - 'key1'=>$randomKey, 'minutesBlocked'=>5, 'numberFailuresAllowed'=>10, 'blackList'=>array()