10.2.dev16 journalisation + blocage brute force login

This commit is contained in:
Fred Tempez 2020-05-30 12:04:45 +02:00
parent 9ce6c8bd9d
commit dd1d3f793f
7 changed files with 120 additions and 47 deletions

View File

@ -39,7 +39,7 @@ class common {
const ACCESS_TIMER = 1800; const ACCESS_TIMER = 1800;
// Numéro de version // Numéro de version
const ZWII_VERSION = '10.2.00.dev14'; const ZWII_VERSION = '10.2.00.dev16';
const ZWII_UPDATE_CHANNEL = "v10"; const ZWII_UPDATE_CHANNEL = "v10";
public static $actions = []; public static $actions = [];
@ -1312,6 +1312,7 @@ class common {
$this->deleteData(['admin','colorButtonText']); $this->deleteData(['admin','colorButtonText']);
$this->setData(['config', 'connect', 'attempt',3]); $this->setData(['config', 'connect', 'attempt',3]);
$this->setData(['config', 'connect', 'timeout',360]); $this->setData(['config', 'connect', 'timeout',360]);
$this->setData(['config', 'connect', 'log',false]);
$this->setData(['core', 'dataVersion', 10200]); $this->setData(['core', 'dataVersion', 10200]);
} }
} }
@ -1581,6 +1582,15 @@ class core extends common {
header('Location:' . helper::baseUrl() . 'install'); header('Location:' . helper::baseUrl() . 'install');
exit(); exit();
} }
// Journalisation
$dataLog = strftime('%d/%m/%y',time()) . ';' . strftime('%R',time()) . ';' ;
$dataLog .= $_SERVER['REMOTE_ADDR'] . ';' ;
$dataLog .= $this->getUser('id') . ';' ;
$dataLog .= $this->getUrl();
$dataLog .= PHP_EOL;
if ($this->getData(['config','connect','log'])) {
file_put_contents(self::DATA_DIR . 'journal.log', $dataLog, FILE_APPEND);
}
// Force la déconnexion des membres bannis ou d'une seconde session // Force la déconnexion des membres bannis ou d'une seconde session
if ( if (
$this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD') $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
@ -1661,7 +1671,6 @@ class core extends common {
$this->setData(['user',$this->getuser('id'),'accessUrl',$this->getUrl()]); $this->setData(['user',$this->getuser('id'),'accessUrl',$this->getUrl()]);
$this->setData(['user',$this->getuser('id'),'accessTimer',time()]); $this->setData(['user',$this->getuser('id'),'accessTimer',time()]);
} }
// Breadcrumb // Breadcrumb
$title = $this->getData(['page', $this->getUrl(0), 'title']); $title = $this->getData(['page', $this->getUrl(0), 'title']);
if (!empty($this->getData(['page', $this->getUrl(0), 'parentPageId'])) && if (!empty($this->getData(['page', $this->getUrl(0), 'parentPageId'])) &&

View File

@ -24,7 +24,9 @@ class config extends common {
'index' => self::GROUP_ADMIN, 'index' => self::GROUP_ADMIN,
'manage' => self::GROUP_ADMIN, 'manage' => self::GROUP_ADMIN,
'updateBaseUrl' => self::GROUP_ADMIN, 'updateBaseUrl' => self::GROUP_ADMIN,
'script' => self::GROUP_ADMIN 'script' => self::GROUP_ADMIN,
'logReset' => self::GROUP_ADMIN,
'logDownload'=> self::GROUP_ADMIN
]; ];
public static $timezones = [ public static $timezones = [
@ -457,6 +459,7 @@ class config extends common {
'connect' => [ 'connect' => [
'attempt' => $this->getInput('configConnectAttempt',helper::FILTER_INT), 'attempt' => $this->getInput('configConnectAttempt',helper::FILTER_INT),
'timeout' => $this->getInput('configConnectTimeout',helper::FILTER_INT), 'timeout' => $this->getInput('configConnectTimeout',helper::FILTER_INT),
'log' => $this->getInput('configConnectLog',helper::FILTER_BOOLEAN)
] ]
] ]
]); ]);
@ -597,6 +600,47 @@ class config extends common {
]); ]);
} }
/**
* Vider le fichier de log
*/
public function logReset() {
unlink(self::DATA_DIR . 'journal.log');
// Valeurs en sortie
$this->addOutput([
'title' => 'Configuration',
'view' => 'index',
'notification' => 'Journal réinitialisé avec succès',
'state' => true
]);
}
/**
* Télécharger le fichier de log
*/
public function logDownload() {
// Creation du ZIP
$fileName = self::DATA_DIR . 'journal.log';
// Téléchargement du ZIP
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Length: ' . filesize($fileName));
readfile( $fileName);
// Valeurs en sortie
$this->addOutput([
'display' => self::DISPLAY_RAW
]);
// Valeurs en sortie
$this->addOutput([
'title' => 'Configuration',
'view' => 'index'
]);
}
/** /**
* Fonction de parcours des données de module * Fonction de parcours des données de module
* @param string $find donnée à rechercher * @param string $find donnée à rechercher

View File

@ -148,7 +148,6 @@
</div> </div>
<div class="col3"> <div class="col3">
<?php echo template::button('configUpdateForced', [ <?php echo template::button('configUpdateForced', [
'class' => 'buttonRed',
'ico' => 'download-cloud', 'ico' => 'download-cloud',
'href' => helper::baseUrl() . 'install/update', 'href' => helper::baseUrl() . 'install/update',
'value' => 'Mise à jour manuelle', 'value' => 'Mise à jour manuelle',
@ -286,27 +285,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col12">
<div class="block">
<h4>Connexion</h4>
<div class="row">
<div class="col4">
<?php echo template::select('configConnectAttempt', $module::$connectAttempt , [
'label' => 'Echecs avant blocage',
'selected' => $this->getData(['config', 'connect', 'attempt'])
]); ?>
</div>
<div class="col4">
<?php echo template::select('configConnectTimeout', $module::$connectTimeout , [
'label' => 'Durée du blocage',
'selected' => $this->getData(['config', 'connect', 'timeout'])
]); ?>
</div>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col12"> <div class="col12">
<div class="block"> <div class="block">
@ -374,7 +352,21 @@
<div class="block"> <div class="block">
<h4>Options avancées</h4> <h4>Options avancées</h4>
<div class="row"> <div class="row">
<div class="col3 offset1">
<?php echo template::select('configConnectAttempt', $module::$connectAttempt , [
'label' => 'Tentatives de logins',
'selected' => $this->getData(['config', 'connect', 'attempt'])
]); ?>
</div>
<div class="col3"> <div class="col3">
<?php echo template::select('configConnectTimeout', $module::$connectTimeout , [
'label' => 'Durée du blocage',
'selected' => $this->getData(['config', 'connect', 'timeout'])
]); ?>
</div>
</div>
<div class="row">
<div class="col3 offset1">
<?php echo template::text('configAnalyticsId', [ <?php echo template::text('configAnalyticsId', [
'help' => 'Saisissez l\'ID de suivi.', 'help' => 'Saisissez l\'ID de suivi.',
'label' => 'Google Analytics', 'label' => 'Google Analytics',
@ -382,21 +374,43 @@
'value' => $this->getData(['config', 'analyticsId']) 'value' => $this->getData(['config', 'analyticsId'])
]); ?> ]); ?>
</div> </div>
<div class="col3 offset1 verticalAlignBottom"> <div class="col3 verticalAlignBottom">
<?php echo template::button('configHead', [ <?php echo template::button('configScriptHead', [
'href' => helper::baseUrl() . 'config/script/head', 'href' => helper::baseUrl() . 'config/script/head',
'value' => 'Script inséré dans head', 'value' => 'Script dans head',
'ico' => 'pencil' 'ico' => 'pencil'
]); ?> ]); ?>
</div> </div>
<div class="col3 offset1 verticalAlignBottom"> <div class="col3 verticalAlignBottom">
<?php echo template::button('scriptBody', [ <?php echo template::button('ConfigScriptBody', [
'href' => helper::baseUrl() . 'config/script/body', 'href' => helper::baseUrl() . 'config/script/body',
'value' => 'Script inséré dans body', 'value' => 'Script dans body',
'ico' => 'pencil' 'ico' => 'pencil'
]); ?> ]); ?>
</div> </div>
</div> </div>
<div class="row">
<div class="col3 offset1 verticalAlignBottom">
<?php echo template::checkbox('configConnectLog', true, 'Activer la journalisation', [
'checked' => $this->getData(['config', 'connect', 'log'])
]); ?>
</div>
<div class="col3">
<?php echo template::button('ConfigLogDownload', [
'href' => helper::baseUrl() . 'config/logDownload',
'value' => 'Téléchargement du journal',
'ico' => 'download'
]); ?>
</div>
<div class="col3">
<?php echo template::button('ConfigLogReset', [
'class' => 'buttonRed',
'href' => helper::baseUrl() . 'config/logReset',
'value' => 'Réinitialisation du journal',
'ico' => 'cancel'
]); ?>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -31,7 +31,8 @@ class init extends common {
], ],
"connect" => [ "connect" => [
"timeout" => 360, "timeout" => 360,
"attempt" => 3 "attempt" => 3,
'log' => false
] ]
], ],
'core' => [ 'core' => [

View File

@ -332,22 +332,17 @@ class user extends common {
// Soumission du formulaire // Soumission du formulaire
if($this->isPost()) { if($this->isPost()) {
$userId = $this->getInput('userLoginId', helper::FILTER_ID, true); $userId = $this->getInput('userLoginId', helper::FILTER_ID, true);
// Contrôle du time out // Contrôle du time out pas de vérification du mot de passe si le temps est dépassé.
if ( $this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) > time() &&
$this->getData(['user',$userId,'connectFail']) > $this->getData(['config', 'connect', 'attempt']) ) {
$this->addOutput([
'notification' => 'Accès bloqué pour ' . $this->getData(['config', 'connect', 'timeout']) . ' minutes'
]);
}
// Connexion si les informations sont correctes // Connexion si les informations sont correctes
if( if( $this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) < time() &&
$this->getData(['user',$userId,'connectFail']) < $this->getData(['config', 'connect', 'attempt']) &&
password_verify($this->getInput('userLoginPassword', helper::FILTER_STRING_SHORT, true), $this->getData(['user', $userId, 'password'])) password_verify($this->getInput('userLoginPassword', helper::FILTER_STRING_SHORT, true), $this->getData(['user', $userId, 'password']))
AND $this->getData(['user', $userId, 'group']) >= self::GROUP_MEMBER AND $this->getData(['user', $userId, 'group']) >= self::GROUP_MEMBER
) { ) {
$expire = $this->getInput('userLoginLongTime') ? strtotime("+1 year") : 0; $expire = $this->getInput('userLoginLongTime') ? strtotime("+1 year") : 0;
setcookie('ZWII_USER_ID', $userId, $expire, helper::baseUrl(false, false)); setcookie('ZWII_USER_ID', $userId, $expire, helper::baseUrl(false, false));
setcookie('ZWII_USER_PASSWORD', $this->getData(['user', $userId, 'password']), $expire, helper::baseUrl(false, false)); setcookie('ZWII_USER_PASSWORD', $this->getData(['user', $userId, 'password']), $expire, helper::baseUrl(false, false));
// Accès multiples avec le m$eme compte // Accès multiples avec le même compte
$this->setData(['user',$userId,'accessCsrf',$_SESSION['csrf']]); $this->setData(['user',$userId,'accessCsrf',$_SESSION['csrf']]);
// Valeurs en sortie lorsque le site est en maintenance et que l'utilisateur n'est pas administrateur // Valeurs en sortie lorsque le site est en maintenance et que l'utilisateur n'est pas administrateur
if( if(
@ -363,7 +358,7 @@ class user extends common {
else { else {
// RAZ compteur échec connexion // RAZ compteur échec connexion
$this->setData(['user',$userId,'connectFail',0 ]); $this->setData(['user',$userId,'connectFail',0 ]);
// Valeurs en sortie en cas de réussite // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'notification' => 'Connexion réussie', 'notification' => 'Connexion réussie',
'redirect' => helper::baseUrl() . str_replace('_', '/', str_replace('__', '#', $this->getUrl(2))), 'redirect' => helper::baseUrl() . str_replace('_', '/', str_replace('__', '#', $this->getUrl(2))),
@ -377,9 +372,9 @@ class user extends common {
if ( is_array($this->getdata(['user',$userId])) ) { if ( is_array($this->getdata(['user',$userId])) ) {
$this->setData(['user',$userId,'connectFail',$this->getdata(['user',$userId,'connectFail']) + 1 ]); $this->setData(['user',$userId,'connectFail',$this->getdata(['user',$userId,'connectFail']) + 1 ]);
} }
// Mettre à jour le timer // Mettre à jour le timer et notifier
if ( $this->getdata(['user',$userId,'connectFail']) > $this->getData(['config', 'connect', 'attempt'])) { if ( $this->getdata(['user',$userId,'connectFail']) > $this->getData(['config', 'connect', 'attempt'])) {
$notification = 'Trop de tentatives, accès bloqué durant ' . $this->getData(['config', 'connect', 'timeout']) . ' minutes.'; $notification = 'Trop de tentatives, accès bloqué durant ' . ($this->getData(['config', 'connect', 'timeout']) / 60) . ' minutes.';
// Ne pas incrémenter le timer si actif // Ne pas incrémenter le timer si actif
if ($this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) < time() ) { if ($this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) < time() ) {
$this->setData(['user',$userId,'connectTimeout', time()]); $this->setData(['user',$userId,'connectTimeout', time()]);
@ -387,6 +382,16 @@ class user extends common {
} else { } else {
$notification = 'Identifiant ou mot de passe incorrect'; $notification = 'Identifiant ou mot de passe incorrect';
} }
// Journalisation
$dataLog = strftime('%d/%m/%y',time()) . ';' . strftime('%R',time()) . ';' ;
$dataLog .= $_SERVER['REMOTE_ADDR'] . ';' ;
$dataLog .= $userId . ';' ;
$dataLog .= $this->getUrl() .';' ;
$dataLog .= 'échec de connexion' ;
$dataLog .= PHP_EOL;
if ($this->getData(['config','connect','log'])) {
file_put_contents(self::DATA_DIR . 'journal.log', $dataLog, FILE_APPEND);
}
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'notification' => $notification 'notification' => $notification

View File

@ -1,10 +1,10 @@
<?php if($module::$articles): ?> <?php if($module::$articles): ?>
<div class="row"> <div class="row">
<div class="col12"> <div class="col12">
<?php foreach($module::$articles as $articleId => $article): ?> <?php foreach($module::$articles as $articleId => $article): ?>
<div class="row rowArticle"> <div class="row rowArticle">
<div class="col3"> <div class="col3">
<?php <?php
// Déterminer le nom de la miniature // Déterminer le nom de la miniature
$parts = explode('/',$article['picture']); $parts = explode('/',$article['picture']);
$thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $article['picture']); $thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $article['picture']);
@ -31,7 +31,7 @@
<?php echo count($article['comment']); ?> <?php echo count($article['comment']); ?>
</a> </a>
<?php echo template::ico('comment', 'left'); ?> <?php echo template::ico('comment', 'left'); ?>
</div> </div>
<div class="blogDate"> <div class="blogDate">
<i class="far fa-calendar-alt"></i> <i class="far fa-calendar-alt"></i>
<?php echo utf8_encode(strftime('%d %B %Y', $article['publishedOn'])); ?> <?php echo utf8_encode(strftime('%d %B %Y', $article['publishedOn'])); ?>

0
site/file/source/screenshot.png Normal file → Executable file
View File