openrat-cms

OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs | README

commit 836eda3c7ca6909883f936544da74be0b8a44d73
parent 12f438d60d0cdd3db8ecde2c91028b13c78e5efe
Author: Jan Dankert <develop@jandankert.de>
Date:   Sat, 28 Nov 2020 00:53:41 +0100

New: Lock password after a number of login fails.

Diffstat:
Mmodules/cms/action/login/LoginLoginAction.class.php | 31++++++++++++++++++++++++++++++-
Mmodules/cms/auth/InternalAuth.class.php | 6++++++
Mmodules/cms/model/User.class.php | 20++++++++++++++++++--
Mmodules/cms/update/Update.class.php | 2+-
Amodules/cms/update/version/DBVersion000023.class.php | 29+++++++++++++++++++++++++++++
Mmodules/language/Language_CN.class.php | 6++++++
Mmodules/language/Language_DE.class.php | 6++++++
Mmodules/language/Language_EN.class.php | 6++++++
Mmodules/language/Language_ES.class.php | 6++++++
Mmodules/language/Language_FR.class.php | 6++++++
Mmodules/language/Language_IT.class.php | 6++++++
Mmodules/language/Language_RU.class.php | 6++++++
Mmodules/language/Messages.class.php | 2++
Mmodules/language/language.yml | 16++++++++++++++++
14 files changed, 144 insertions(+), 4 deletions(-)

diff --git a/modules/cms/action/login/LoginLoginAction.class.php b/modules/cms/action/login/LoginLoginAction.class.php @@ -6,9 +6,9 @@ use cms\action\Method; use cms\action\RequestParams; use cms\auth\Auth; use cms\auth\AuthRunner; -use cms\auth\InternalAuth; use cms\base\Configuration; use cms\base\DB; +use cms\base\Startup; use cms\model\User; use language\Language; use language\Messages; @@ -153,6 +153,35 @@ class LoginLoginAction extends LoginAction implements Method { $this->addErrorFor( null,Messages::LOGIN_FAILED, ['name' => $loginName] ); $this->addValidationError('login_name' , ''); $this->addValidationError('login_password', ''); + + // Increase fail counter + $user = User::loadWithName($loginName,User::AUTH_TYPE_INTERNAL); + + $isLocked = $user->passwordLockedUntil && $user->passwordLockedUntil > Startup::getStartTime(); + + if ( ! $isLocked ) { + + $user->increaseFailedPasswordCounter(); + + $lockAfter = Configuration::subset(['security','password'])->get('lock_after_fail_count',10); + if ( $lockAfter && $user->passwordFailedCount % $lockAfter == 0 ) { + $factor = pow(2, intval($user->passwordFailedCount/$lockAfter) - 1 ) ; + $lockedDuration = Configuration::subset(['security','password'])->get('lock_duration',120) * $factor * 60; + + $lockedUntil = Startup::getStartTime() + $lockedDuration; + $user->passwordLockedUntil = $lockedUntil; + $user->persist(); + + if ( $user->mail ) { + $mail = new Mail( $user->mail,Messages::MAIL_PASSWORD_LOCKED_SUBJECT,Messages::MAIL_PASSWORD_LOCKED); + $mail->setVar('username',$user->name); + $mail->setVar('name',$user->getName() ); + $mail->setVar('until',date( \cms\base\Language::lang(Messages::DATE_FORMAT ), $lockedUntil ) ); + $mail->send(); + } + } + } + return; } diff --git a/modules/cms/auth/InternalAuth.class.php b/modules/cms/auth/InternalAuth.class.php @@ -4,6 +4,7 @@ namespace cms\auth; use cms\base\Configuration; use cms\base\DB as Db; +use cms\base\Startup; use cms\model\User; use LogicException; use security\Password; @@ -40,6 +41,11 @@ SQL return null; } + $lockedUntil = $row_user['password_locked_until']; + if ( $lockedUntil && $lockedUntil > Startup::getStartTime() ) { + return Auth::STATUS_FAILED; // Password is locked + } + // Pruefen ob Kennwort mit Datenbank uebereinstimmt. if (!Password::check(User::pepperPassword($password), $row_user['password_hash'], $row_user['password_algo'])) { return Auth::STATUS_FAILED; diff --git a/modules/cms/model/User.class.php b/modules/cms/model/User.class.php @@ -76,6 +76,10 @@ class User extends ModelBase public $issuer = null; public $type = User::AUTH_TYPE_INTERNAL; + public $passwordFailedCount = 0; + public $passwordLockedUntil = 0; + + // Konstruktor public function __construct( $userid='' ) { @@ -421,6 +425,8 @@ SQL $this->totp = ($row['totp']==1); $this->passwordExpires = $row['password_expires']; $this->passwordAlgo = $row['password_algo']; + $this->passwordLockedUntil = $row['password_locked_until']; + $this->passwordFailedCount = $row['password_fail_count' ]; $this->type = $row['auth_type']; $this->issuer = $row['issuer']; @@ -486,7 +492,9 @@ SQL timezone = {timezone}, is_admin = {isAdmin}, totp = {totp}, - hotp = {hotp} + hotp = {hotp}, + password_fail_count = {fail_count}, + password_locked_until = {locked_until} WHERE id={userid} SQL ); @@ -502,7 +510,9 @@ SQL $sql->setBoolean( 'totp' ,$this->totp ); $sql->setBoolean( 'hotp' ,$this->hotp ); $sql->setInt ( 'userid' ,$this->userid ); - + $sql->setInt ( 'fail_count' ,$this->passwordFailedCount ); + $sql->setInt ( 'locked_until',$this->passwordLockedUntil ); + // Datenbankabfrage ausfuehren $sql->query(); } @@ -1144,6 +1154,12 @@ SQL } + public function increaseFailedPasswordCounter() { + $this->passwordFailedCount++; + $this->save(); + } + + public function getId() { return $this->userid; diff --git a/modules/cms/update/Update.class.php b/modules/cms/update/Update.class.php @@ -12,7 +12,7 @@ use logger\Logger; class Update { // This is the required DB version: - const SUPPORTED_VERSION = 22; + const SUPPORTED_VERSION = 23; // -----------------------^^----------------------------- const STATUS_UPDATE_PROGRESS = 0; diff --git a/modules/cms/update/version/DBVersion000023.class.php b/modules/cms/update/version/DBVersion000023.class.php @@ -0,0 +1,29 @@ +<?php + +namespace cms\update\version; + +use database\DbVersion; +use database\Column; + +/** + * Security enhancements: + * - log login tries + * - add new fail counter + * + * @author Jan Dankert + * + */ +class DBVersion000023 extends DbVersion +{ + /** + * + */ + public function update() + { + $table = $this->table('user'); + + $table->column('password_locked_until' )->type(Column::TYPE_INT )->nullable()->add(); + $table->column('password_fail_count' )->type(Column::TYPE_INT )->defaultValue(0 )->add(); + } +} + diff --git a/modules/language/Language_CN.class.php b/modules/language/Language_CN.class.php @@ -560,6 +560,12 @@ public function get() { return [ We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Your password is locked', +'MAIL_PASSWORD_LOCKED'=>'Hello ${name}, + +you have tried to login a few times with your user account ${username}. + +Due to security resons your account is locked until ${until}.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Change of your E-Mail adress', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Password change request', 'MAIL_SUBJECT_PASSWORD_NEW'=>'New password', diff --git a/modules/language/Language_DE.class.php b/modules/language/Language_DE.class.php @@ -560,6 +560,12 @@ public function get() { return [ Sie haben sich mit Ihrem Benutzernamen ${username} auf dem Gerät ${browser} (${platform}) neu angemeldet. Dies dient rein zu Ihrer Information. Sofern Sie dieses nicht waren, ändern Sie bitte umgehend Ihr Kennwort.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Kennwort gesperrt', +'MAIL_PASSWORD_LOCKED'=>'Guten Tag ${name}, + +Sie haben mehrfach vergeblich versucht, sich mit Ihrem Benutzernamen ${username} anzumelden. + +Aus Sicherheitsgründen ist Ihre Kennung bis ${until} gesperrt.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Änderung Ihrer E-Mail-Adresse', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Bestaetigung fuer Kennwortänderung', 'MAIL_SUBJECT_PASSWORD_NEW'=>'Neues Kennwort', diff --git a/modules/language/Language_EN.class.php b/modules/language/Language_EN.class.php @@ -560,6 +560,12 @@ public function get() { return [ We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Your password is locked', +'MAIL_PASSWORD_LOCKED'=>'Hello ${name}, + +you have tried to login a few times with your user account ${username}. + +Due to security resons your account is locked until ${until}.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Change of your E-Mail adress', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Password change request', 'MAIL_SUBJECT_PASSWORD_NEW'=>'New password', diff --git a/modules/language/Language_ES.class.php b/modules/language/Language_ES.class.php @@ -560,6 +560,12 @@ public function get() { return [ We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Your password is locked', +'MAIL_PASSWORD_LOCKED'=>'Hello ${name}, + +you have tried to login a few times with your user account ${username}. + +Due to security resons your account is locked until ${until}.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Change of your E-Mail adress', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Demande de changement de mot de passe', 'MAIL_SUBJECT_PASSWORD_NEW'=>'Nouveau mot de passe', diff --git a/modules/language/Language_FR.class.php b/modules/language/Language_FR.class.php @@ -560,6 +560,12 @@ public function get() { return [ We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Your password is locked', +'MAIL_PASSWORD_LOCKED'=>'Hello ${name}, + +you have tried to login a few times with your user account ${username}. + +Due to security resons your account is locked until ${until}.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Change of your E-Mail adress', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Demande de changement de mot de passe', 'MAIL_SUBJECT_PASSWORD_NEW'=>'Nouveau mot de passe', diff --git a/modules/language/Language_IT.class.php b/modules/language/Language_IT.class.php @@ -560,6 +560,12 @@ public function get() { return [ We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Your password is locked', +'MAIL_PASSWORD_LOCKED'=>'Hello ${name}, + +you have tried to login a few times with your user account ${username}. + +Due to security resons your account is locked until ${until}.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Change of your E-Mail adress', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Contraseña de la petición del cambio de la contraseña nueva', 'MAIL_SUBJECT_PASSWORD_NEW'=>'New password', diff --git a/modules/language/Language_RU.class.php b/modules/language/Language_RU.class.php @@ -560,6 +560,12 @@ public function get() { return [ We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password.', +'MAIL_PASSWORD_LOCKED_SUBJECT'=>'Your password is locked', +'MAIL_PASSWORD_LOCKED'=>'Hello ${name}, + +you have tried to login a few times with your user account ${username}. + +Due to security resons your account is locked until ${until}.', 'MAIL_SUBJECT_MAIL_CHANGE_CODE'=>'Change of your E-Mail adress', 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'=>'Запрос на изменение пароля', 'MAIL_SUBJECT_PASSWORD_NEW'=>'Новый пароль', diff --git a/modules/language/Messages.class.php b/modules/language/Messages.class.php @@ -556,6 +556,8 @@ class Messages { const MAIL_PASSWORD_CHANGE_SUCCESS = 'MAIL_PASSWORD_CHANGE_SUCCESS'; const MAIL_NEW_LOGIN_SUBJECT = 'MAIL_NEW_LOGIN_SUBJECT'; const MAIL_NEW_LOGIN_TEXT = 'MAIL_NEW_LOGIN_TEXT'; + const MAIL_PASSWORD_LOCKED_SUBJECT = 'MAIL_PASSWORD_LOCKED_SUBJECT'; + const MAIL_PASSWORD_LOCKED = 'MAIL_PASSWORD_LOCKED'; const MAIL_SUBJECT_MAIL_CHANGE_CODE = 'MAIL_SUBJECT_MAIL_CHANGE_CODE'; const MAIL_SUBJECT_PASSWORD_COMMIT_CODE = 'MAIL_SUBJECT_PASSWORD_COMMIT_CODE'; const MAIL_SUBJECT_PASSWORD_NEW = 'MAIL_SUBJECT_PASSWORD_NEW'; diff --git a/modules/language/language.yml b/modules/language/language.yml @@ -2846,6 +2846,22 @@ MAIL_NEW_LOGIN_TEXT: We want to inform you that you just logged in with your username ${username} on the device ${browser} (${platform}). If you did not do this, please change your password. +MAIL_PASSWORD_LOCKED_SUBJECT: + de: Kennwort gesperrt + en: Your password is locked +MAIL_PASSWORD_LOCKED: + de: | + Guten Tag ${name}, + + Sie haben mehrfach vergeblich versucht, sich mit Ihrem Benutzernamen ${username} anzumelden. + + Aus Sicherheitsgründen ist Ihre Kennung bis ${until} gesperrt. + en: | + Hello ${name}, + + you have tried to login a few times with your user account ${username}. + + Due to security resons your account is locked until ${until}. MAIL_SUBJECT_MAIL_CHANGE_CODE: de: Änderung Ihrer E-Mail-Adresse en: Change of your E-Mail adress