diff options
author | ArthurHoaro <arthur@hoa.ro> | 2020-11-12 13:11:07 +0100 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-11-12 13:11:07 +0100 |
commit | af50eba28a7bd286de4c8c9ee6dc5216b915d149 (patch) | |
tree | ffa30a9358e82d27be75d8fc5e57f3c8820dc6d3 /application/security | |
parent | b6f678a5a1d15acf284ebcec16c905e976671ce1 (diff) | |
parent | 1409f1c89a7ca01456ae2dcd6357d296e2b99f5a (diff) | |
download | Shaarli-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.php | 32 | ||||
-rw-r--r-- | application/security/LoginManager.php | 83 | ||||
-rw-r--r-- | application/security/SessionManager.php | 10 |
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 | |||
4 | namespace Shaarli\Security; | 3 | namespace Shaarli\Security; |
5 | 4 | ||
6 | use Shaarli\FileUtils; | 5 | use Psr\Log\LoggerInterface; |
6 | use 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 | |||
2 | namespace Shaarli\Security; | 3 | namespace Shaarli\Security; |
3 | 4 | ||
4 | use Exception; | 5 | use Exception; |
6 | use Psr\Log\LoggerInterface; | ||
5 | use Shaarli\Config\ConfigManager; | 7 | use 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 | |||
2 | namespace Shaarli\Security; | 3 | namespace Shaarli\Security; |
3 | 4 | ||
4 | use Shaarli\Config\ConfigManager; | 5 | use 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 |