aboutsummaryrefslogtreecommitdiffhomepage
path: root/index.php
diff options
context:
space:
mode:
authorVirtualTam <virtualtam@flibidi.net>2017-10-25 23:03:31 +0200
committerVirtualTam <virtualtam@flibidi.net>2018-02-05 18:12:09 +0100
commit44acf706812bc77812e6648c2cc28af36e172a14 (patch)
tree2c211d422b9d6c27ab341644531913361b6f1024 /index.php
parenta381c373b30ed04001ea31ff5c38e077edacaf18 (diff)
downloadShaarli-44acf706812bc77812e6648c2cc28af36e172a14.tar.gz
Shaarli-44acf706812bc77812e6648c2cc28af36e172a14.tar.zst
Shaarli-44acf706812bc77812e6648c2cc28af36e172a14.zip
Refactor login / ban authentication steps
Relates to https://github.com/shaarli/Shaarli/issues/324 Added: - Add the `LoginManager` class to manage logins and bans Changed: - Refactor IP ban management - Simplify logic - Avoid using globals, inject dependencies Fixed: - Use `ban_duration` instead of `ban_after` when setting a new ban Signed-off-by: VirtualTam <virtualtam@flibidi.net>
Diffstat (limited to 'index.php')
-rw-r--r--index.php116
1 files changed, 16 insertions, 100 deletions
diff --git a/index.php b/index.php
index 067b8fcb..91c3f07e 100644
--- a/index.php
+++ b/index.php
@@ -78,6 +78,7 @@ require_once 'application/Updater.php';
78use \Shaarli\Languages; 78use \Shaarli\Languages;
79use \Shaarli\ThemeUtils; 79use \Shaarli\ThemeUtils;
80use \Shaarli\Config\ConfigManager; 80use \Shaarli\Config\ConfigManager;
81use \Shaarli\LoginManager;
81use \Shaarli\SessionManager; 82use \Shaarli\SessionManager;
82 83
83// Ensure the PHP version is supported 84// Ensure the PHP version is supported
@@ -122,6 +123,7 @@ if (isset($_COOKIE['shaarli']) && !SessionManager::checkId($_COOKIE['shaarli']))
122} 123}
123 124
124$conf = new ConfigManager(); 125$conf = new ConfigManager();
126$loginManager = new LoginManager($GLOBALS, $conf);
125$sessionManager = new SessionManager($_SESSION, $conf); 127$sessionManager = new SessionManager($_SESSION, $conf);
126 128
127// LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead. 129// LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead.
@@ -293,108 +295,22 @@ function logout() {
293 setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); 295 setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH);
294} 296}
295 297
296
297// ------------------------------------------------------------------------------------------
298// Brute force protection system
299// Several consecutive failed logins will ban the IP address for 30 minutes.
300if (!is_file($conf->get('resource.ban_file', 'data/ipbans.php'))) {
301 // FIXME! globals
302 file_put_contents(
303 $conf->get('resource.ban_file', 'data/ipbans.php'),
304 "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(),'BANS'=>array()),true).";\n?>"
305 );
306}
307include $conf->get('resource.ban_file', 'data/ipbans.php');
308/**
309 * Signal a failed login. Will ban the IP if too many failures:
310 *
311 * @param ConfigManager $conf Configuration Manager instance.
312 */
313function ban_loginFailed($conf)
314{
315 $ip = $_SERVER['REMOTE_ADDR'];
316 $trusted = $conf->get('security.trusted_proxies', array());
317 if (in_array($ip, $trusted)) {
318 $ip = getIpAddressFromProxy($_SERVER, $trusted);
319 if (!$ip) {
320 return;
321 }
322 }
323 $gb = $GLOBALS['IPBANS'];
324 if (! isset($gb['FAILURES'][$ip])) {
325 $gb['FAILURES'][$ip]=0;
326 }
327 $gb['FAILURES'][$ip]++;
328 if ($gb['FAILURES'][$ip] > ($conf->get('security.ban_after') - 1))
329 {
330 $gb['BANS'][$ip] = time() + $conf->get('security.ban_after', 1800);
331 logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'IP address banned from login');
332 }
333 $GLOBALS['IPBANS'] = $gb;
334 file_put_contents(
335 $conf->get('resource.ban_file', 'data/ipbans.php'),
336 "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>"
337 );
338}
339
340/**
341 * Signals a successful login. Resets failed login counter.
342 *
343 * @param ConfigManager $conf Configuration Manager instance.
344 */
345function ban_loginOk($conf)
346{
347 $ip = $_SERVER['REMOTE_ADDR'];
348 $gb = $GLOBALS['IPBANS'];
349 unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
350 $GLOBALS['IPBANS'] = $gb;
351 file_put_contents(
352 $conf->get('resource.ban_file', 'data/ipbans.php'),
353 "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>"
354 );
355}
356
357/**
358 * Checks if the user CAN login. If 'true', the user can try to login.
359 *
360 * @param ConfigManager $conf Configuration Manager instance.
361 *
362 * @return bool: true if the user is allowed to login.
363 */
364function ban_canLogin($conf)
365{
366 $ip=$_SERVER["REMOTE_ADDR"]; $gb=$GLOBALS['IPBANS'];
367 if (isset($gb['BANS'][$ip]))
368 {
369 // User is banned. Check if the ban has expired:
370 if ($gb['BANS'][$ip]<=time())
371 { // Ban expired, user can try to login again.
372 logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Ban lifted.');
373 unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
374 file_put_contents(
375 $conf->get('resource.ban_file', 'data/ipbans.php'),
376 "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>"
377 );
378 return true; // Ban has expired, user can login.
379 }
380 return false; // User is banned.
381 }
382 return true; // User is not banned.
383}
384
385// ------------------------------------------------------------------------------------------ 298// ------------------------------------------------------------------------------------------
386// Process login form: Check if login/password is correct. 299// Process login form: Check if login/password is correct.
387if (isset($_POST['login'])) 300if (isset($_POST['login']))
388{ 301{
389 if (!ban_canLogin($conf)) die(t('I said: NO. You are banned for the moment. Go away.')); 302 if (! $loginManager->canLogin($_SERVER)) {
303 die(t('I said: NO. You are banned for the moment. Go away.'));
304 }
390 if (isset($_POST['password']) 305 if (isset($_POST['password'])
391 && $sessionManager->checkToken($_POST['token']) 306 && $sessionManager->checkToken($_POST['token'])
392 && (check_auth($_POST['login'], $_POST['password'], $conf)) 307 && (check_auth($_POST['login'], $_POST['password'], $conf))
393 ) { // Login/password is OK. 308 ) {
394 ban_loginOk($conf); 309 // Login/password is OK.
310 $loginManager->handleSuccessfulLogin($_SERVER);
311
395 // If user wants to keep the session cookie even after the browser closes: 312 // If user wants to keep the session cookie even after the browser closes:
396 if (!empty($_POST['longlastingsession'])) 313 if (!empty($_POST['longlastingsession'])) {
397 {
398 $_SESSION['longlastingsession'] = 31536000; // (31536000 seconds = 1 year) 314 $_SESSION['longlastingsession'] = 31536000; // (31536000 seconds = 1 year)
399 $expiration = time() + $_SESSION['longlastingsession']; // calculate relative cookie expiration (1 year from now) 315 $expiration = time() + $_SESSION['longlastingsession']; // calculate relative cookie expiration (1 year from now)
400 setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, $expiration, WEB_PATH); 316 setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, $expiration, WEB_PATH);
@@ -437,10 +353,8 @@ if (isset($_POST['login']))
437 } 353 }
438 } 354 }
439 header('Location: ?'); exit; 355 header('Location: ?'); exit;
440 } 356 } else {
441 else 357 $loginManager->handleFailedLogin($_SERVER);
442 {
443 ban_loginFailed($conf);
444 $redir = '&username='. urlencode($_POST['login']); 358 $redir = '&username='. urlencode($_POST['login']);
445 if (isset($_GET['post'])) { 359 if (isset($_GET['post'])) {
446 $redir .= '&post=' . urlencode($_GET['post']); 360 $redir .= '&post=' . urlencode($_GET['post']);
@@ -684,8 +598,9 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) {
684 * @param LinkDB $LINKSDB 598 * @param LinkDB $LINKSDB
685 * @param History $history instance 599 * @param History $history instance
686 * @param SessionManager $sessionManager SessionManager instance 600 * @param SessionManager $sessionManager SessionManager instance
601 * @param LoginManager $loginManager LoginManager instance
687 */ 602 */
688function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) 603function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $loginManager)
689{ 604{
690 $updater = new Updater( 605 $updater = new Updater(
691 read_updates_file($conf->get('resource.updates')), 606 read_updates_file($conf->get('resource.updates')),
@@ -761,6 +676,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager)
761 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); 676 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
762 // add default state of the 'remember me' checkbox 677 // add default state of the 'remember me' checkbox
763 $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default')); 678 $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default'));
679 $PAGE->assign('user_can_login', $loginManager->canLogin($_SERVER));
764 $PAGE->renderPage('loginform'); 680 $PAGE->renderPage('loginform');
765 exit; 681 exit;
766 } 682 }
@@ -2330,7 +2246,7 @@ $response = $app->run(true);
2330if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { 2246if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) {
2331 // We use UTF-8 for proper international characters handling. 2247 // We use UTF-8 for proper international characters handling.
2332 header('Content-Type: text/html; charset=utf-8'); 2248 header('Content-Type: text/html; charset=utf-8');
2333 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager); 2249 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager);
2334} else { 2250} else {
2335 $app->respond($response); 2251 $app->respond($response);
2336} 2252}