aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/security
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-11-12 13:11:07 +0100
committerArthurHoaro <arthur@hoa.ro>2020-11-12 13:11:07 +0100
commitaf50eba28a7bd286de4c8c9ee6dc5216b915d149 (patch)
treeffa30a9358e82d27be75d8fc5e57f3c8820dc6d3 /application/security
parentb6f678a5a1d15acf284ebcec16c905e976671ce1 (diff)
parent1409f1c89a7ca01456ae2dcd6357d296e2b99f5a (diff)
downloadShaarli-latest.tar.gz
Shaarli-latest.tar.zst
Shaarli-latest.zip
Merge tag 'v0.12.1' into latestlatest
v0.12.1
Diffstat (limited to 'application/security')
-rw-r--r--application/security/BanManager.php32
-rw-r--r--application/security/LoginManager.php83
-rw-r--r--application/security/SessionManager.php10
3 files changed, 61 insertions, 64 deletions
diff --git a/application/security/BanManager.php b/application/security/BanManager.php
index 68190c54..7077af5b 100644
--- a/application/security/BanManager.php
+++ b/application/security/BanManager.php
@@ -1,9 +1,9 @@
1<?php 1<?php
2 2
3
4namespace Shaarli\Security; 3namespace Shaarli\Security;
5 4
6use Shaarli\FileUtils; 5use Psr\Log\LoggerInterface;
6use Shaarli\Helper\FileUtils;
7 7
8/** 8/**
9 * Class BanManager 9 * Class BanManager
@@ -28,8 +28,8 @@ class BanManager
28 /** @var string Path to the file containing IP bans and failures */ 28 /** @var string Path to the file containing IP bans and failures */
29 protected $banFile; 29 protected $banFile;
30 30
31 /** @var string Path to the log file, used to log bans */ 31 /** @var LoggerInterface Path to the log file, used to log bans */
32 protected $logFile; 32 protected $logger;
33 33
34 /** @var array List of IP with their associated number of failed attempts */ 34 /** @var array List of IP with their associated number of failed attempts */
35 protected $failures = []; 35 protected $failures = [];
@@ -40,18 +40,20 @@ class BanManager
40 /** 40 /**
41 * BanManager constructor. 41 * BanManager constructor.
42 * 42 *
43 * @param array $trustedProxies List of allowed proxies IP 43 * @param array $trustedProxies List of allowed proxies IP
44 * @param int $nbAttempts Number of allowed failed attempt before the ban 44 * @param int $nbAttempts Number of allowed failed attempt before the ban
45 * @param int $banDuration Ban duration in seconds 45 * @param int $banDuration Ban duration in seconds
46 * @param string $banFile Path to the file containing IP bans and failures 46 * @param string $banFile Path to the file containing IP bans and failures
47 * @param string $logFile Path to the log file, used to log bans 47 * @param LoggerInterface $logger PSR-3 logger to save login attempts in log directory
48 */ 48 */
49 public function __construct($trustedProxies, $nbAttempts, $banDuration, $banFile, $logFile) { 49 public function __construct($trustedProxies, $nbAttempts, $banDuration, $banFile, LoggerInterface $logger)
50 {
50 $this->trustedProxies = $trustedProxies; 51 $this->trustedProxies = $trustedProxies;
51 $this->nbAttempts = $nbAttempts; 52 $this->nbAttempts = $nbAttempts;
52 $this->banDuration = $banDuration; 53 $this->banDuration = $banDuration;
53 $this->banFile = $banFile; 54 $this->banFile = $banFile;
54 $this->logFile = $logFile; 55 $this->logger = $logger;
56
55 $this->readBanFile(); 57 $this->readBanFile();
56 } 58 }
57 59
@@ -78,11 +80,7 @@ class BanManager
78 80
79 if ($this->failures[$ip] >= $this->nbAttempts) { 81 if ($this->failures[$ip] >= $this->nbAttempts) {
80 $this->bans[$ip] = time() + $this->banDuration; 82 $this->bans[$ip] = time() + $this->banDuration;
81 logm( 83 $this->logger->info(format_log('IP address banned from login: ' . $ip, $ip));
82 $this->logFile,
83 $server['REMOTE_ADDR'],
84 'IP address banned from login: '. $ip
85 );
86 } 84 }
87 $this->writeBanFile(); 85 $this->writeBanFile();
88 } 86 }
@@ -138,7 +136,7 @@ class BanManager
138 unset($this->failures[$ip]); 136 unset($this->failures[$ip]);
139 } 137 }
140 unset($this->bans[$ip]); 138 unset($this->bans[$ip]);
141 logm($this->logFile, $server['REMOTE_ADDR'], 'Ban lifted for: '. $ip); 139 $this->logger->info(format_log('Ban lifted for: ' . $ip, $ip));
142 140
143 $this->writeBanFile(); 141 $this->writeBanFile();
144 return false; 142 return false;
diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php
index d74c3118..b795b80e 100644
--- a/application/security/LoginManager.php
+++ b/application/security/LoginManager.php
@@ -1,7 +1,9 @@
1<?php 1<?php
2
2namespace Shaarli\Security; 3namespace Shaarli\Security;
3 4
4use Exception; 5use Exception;
6use Psr\Log\LoggerInterface;
5use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
6 8
7/** 9/**
@@ -31,26 +33,30 @@ class LoginManager
31 protected $staySignedInToken = ''; 33 protected $staySignedInToken = '';
32 /** @var CookieManager */ 34 /** @var CookieManager */
33 protected $cookieManager; 35 protected $cookieManager;
36 /** @var LoggerInterface */
37 protected $logger;
34 38
35 /** 39 /**
36 * Constructor 40 * Constructor
37 * 41 *
38 * @param ConfigManager $configManager Configuration Manager instance 42 * @param ConfigManager $configManager Configuration Manager instance
39 * @param SessionManager $sessionManager SessionManager instance 43 * @param SessionManager $sessionManager SessionManager instance
40 * @param CookieManager $cookieManager CookieManager instance 44 * @param CookieManager $cookieManager CookieManager instance
45 * @param BanManager $banManager
46 * @param LoggerInterface $logger Used to log login attempts
41 */ 47 */
42 public function __construct($configManager, $sessionManager, $cookieManager) 48 public function __construct(
43 { 49 ConfigManager $configManager,
50 SessionManager $sessionManager,
51 CookieManager $cookieManager,
52 BanManager $banManager,
53 LoggerInterface $logger
54 ) {
44 $this->configManager = $configManager; 55 $this->configManager = $configManager;
45 $this->sessionManager = $sessionManager; 56 $this->sessionManager = $sessionManager;
46 $this->cookieManager = $cookieManager; 57 $this->cookieManager = $cookieManager;
47 $this->banManager = new BanManager( 58 $this->banManager = $banManager;
48 $this->configManager->get('security.trusted_proxies', []), 59 $this->logger = $logger;
49 $this->configManager->get('security.ban_after'),
50 $this->configManager->get('security.ban_duration'),
51 $this->configManager->get('resource.ban_file', 'data/ipbans.php'),
52 $this->configManager->get('resource.log')
53 );
54 60
55 if ($this->configManager->get('security.open_shaarli') === true) { 61 if ($this->configManager->get('security.open_shaarli') === true) {
56 $this->openShaarli = true; 62 $this->openShaarli = true;
@@ -101,7 +107,8 @@ class LoginManager
101 // The user client has a valid stay-signed-in cookie 107 // The user client has a valid stay-signed-in cookie
102 // Session information is updated with the current client information 108 // Session information is updated with the current client information
103 $this->sessionManager->storeLoginInfo($clientIpId); 109 $this->sessionManager->storeLoginInfo($clientIpId);
104 } elseif ($this->sessionManager->hasSessionExpired() 110 } elseif (
111 $this->sessionManager->hasSessionExpired()
105 || $this->sessionManager->hasClientIpChanged($clientIpId) 112 || $this->sessionManager->hasClientIpChanged($clientIpId)
106 ) { 113 ) {
107 $this->sessionManager->logout(); 114 $this->sessionManager->logout();
@@ -118,7 +125,7 @@ class LoginManager
118 * 125 *
119 * @return true when the user is logged in, false otherwise 126 * @return true when the user is logged in, false otherwise
120 */ 127 */
121 public function isLoggedIn() 128 public function isLoggedIn(): bool
122 { 129 {
123 if ($this->openShaarli) { 130 if ($this->openShaarli) {
124 return true; 131 return true;
@@ -129,48 +136,35 @@ class LoginManager
129 /** 136 /**
130 * Check user credentials are valid 137 * Check user credentials are valid
131 * 138 *
132 * @param string $remoteIp Remote client IP address
133 * @param string $clientIpId Client IP address identifier 139 * @param string $clientIpId Client IP address identifier
134 * @param string $login Username 140 * @param string $login Username
135 * @param string $password Password 141 * @param string $password Password
136 * 142 *
137 * @return bool true if the provided credentials are valid, false otherwise 143 * @return bool true if the provided credentials are valid, false otherwise
138 */ 144 */
139 public function checkCredentials($remoteIp, $clientIpId, $login, $password) 145 public function checkCredentials($clientIpId, $login, $password)
140 { 146 {
141 // Check login matches config
142 if ($login !== $this->configManager->get('credentials.login')) {
143 return false;
144 }
145
146 // Check credentials 147 // Check credentials
147 try { 148 try {
148 $useLdapLogin = !empty($this->configManager->get('ldap.host')); 149 $useLdapLogin = !empty($this->configManager->get('ldap.host'));
149 if ((false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password)) 150 if (
150 || (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password)) 151 $login === $this->configManager->get('credentials.login')
152 && (
153 (false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password))
154 || (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password))
155 )
151 ) { 156 ) {
152 $this->sessionManager->storeLoginInfo($clientIpId); 157 $this->sessionManager->storeLoginInfo($clientIpId);
153 logm( 158 $this->logger->info(format_log('Login successful', $clientIpId));
154 $this->configManager->get('resource.log'), 159
155 $remoteIp, 160 return true;
156 'Login successful'
157 );
158 return true;
159 } 161 }
160 } 162 } catch (Exception $exception) {
161 catch(Exception $exception) { 163 $this->logger->info(format_log('Exception while checking credentials: ' . $exception, $clientIpId));
162 logm(
163 $this->configManager->get('resource.log'),
164 $remoteIp,
165 'Exception while checking credentials: ' . $exception
166 );
167 } 164 }
168 165
169 logm( 166 $this->logger->info(format_log('Login failed for user ' . $login, $clientIpId));
170 $this->configManager->get('resource.log'), 167
171 $remoteIp,
172 'Login failed for user ' . $login
173 );
174 return false; 168 return false;
175 } 169 }
176 170
@@ -183,7 +177,8 @@ class LoginManager
183 * 177 *
184 * @return bool true if the provided credentials are valid, false otherwise 178 * @return bool true if the provided credentials are valid, false otherwise
185 */ 179 */
186 public function checkCredentialsFromLocalConfig($login, $password) { 180 public function checkCredentialsFromLocalConfig($login, $password)
181 {
187 $hash = sha1($password . $login . $this->configManager->get('credentials.salt')); 182 $hash = sha1($password . $login . $this->configManager->get('credentials.salt'));
188 183
189 return $login == $this->configManager->get('credentials.login') 184 return $login == $this->configManager->get('credentials.login')
@@ -202,14 +197,14 @@ class LoginManager
202 */ 197 */
203 public function checkCredentialsFromLdap($login, $password, $connect = null, $bind = null) 198 public function checkCredentialsFromLdap($login, $password, $connect = null, $bind = null)
204 { 199 {
205 $connect = $connect ?? function($host) { 200 $connect = $connect ?? function ($host) {
206 $resource = ldap_connect($host); 201 $resource = ldap_connect($host);
207 202
208 ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3); 203 ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3);
209 204
210 return $resource; 205 return $resource;
211 }; 206 };
212 $bind = $bind ?? function($handle, $dn, $password) { 207 $bind = $bind ?? function ($handle, $dn, $password) {
213 return ldap_bind($handle, $dn, $password); 208 return ldap_bind($handle, $dn, $password);
214 }; 209 };
215 210
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php
index 36df8c1c..f957b91a 100644
--- a/application/security/SessionManager.php
+++ b/application/security/SessionManager.php
@@ -1,4 +1,5 @@
1<?php 1<?php
2
2namespace Shaarli\Security; 3namespace Shaarli\Security;
3 4
4use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
@@ -79,7 +80,7 @@ class SessionManager
79 */ 80 */
80 public function generateToken() 81 public function generateToken()
81 { 82 {
82 $token = sha1(uniqid('', true) .'_'. mt_rand() . $this->conf->get('credentials.salt')); 83 $token = sha1(uniqid('', true) . '_' . mt_rand() . $this->conf->get('credentials.salt'));
83 $this->session['tokens'][$token] = 1; 84 $this->session['tokens'][$token] = 1;
84 return $token; 85 return $token;
85 } 86 }
@@ -293,9 +294,12 @@ class SessionManager
293 return session_start(); 294 return session_start();
294 } 295 }
295 296
296 public function cookieParameters(int $lifeTime, string $path, string $domain): bool 297 /**
298 * Be careful, return type of session_set_cookie_params() changed between PHP 7.1 and 7.2.
299 */
300 public function cookieParameters(int $lifeTime, string $path, string $domain): void
297 { 301 {
298 return session_set_cookie_params($lifeTime, $path, $domain); 302 session_set_cookie_params($lifeTime, $path, $domain);
299 } 303 }
300 304
301 public function regenerateId(bool $deleteOldSession = false): bool 305 public function regenerateId(bool $deleteOldSession = false): bool