]>
Commit | Line | Data |
---|---|---|
ebd650c0 | 1 | <?php |
53054b2b | 2 | |
fab87c26 | 3 | namespace Shaarli\Security; |
ebd650c0 | 4 | |
c7721487 V |
5 | use Shaarli\Config\ConfigManager; |
6 | ||
ebd650c0 V |
7 | /** |
8 | * Manages the server-side session | |
9 | */ | |
10 | class SessionManager | |
11 | { | |
af290059 A |
12 | public const KEY_LINKS_PER_PAGE = 'LINKS_PER_PAGE'; |
13 | public const KEY_VISIBILITY = 'visibility'; | |
14 | public const KEY_UNTAGGED_ONLY = 'untaggedonly'; | |
15 | ||
ef00f9d2 A |
16 | public const KEY_SUCCESS_MESSAGES = 'successes'; |
17 | public const KEY_WARNING_MESSAGES = 'warnings'; | |
18 | public const KEY_ERROR_MESSAGES = 'errors'; | |
19 | ||
c7721487 | 20 | /** @var int Session expiration timeout, in seconds */ |
51f0128c V |
21 | public static $SHORT_TIMEOUT = 3600; // 1 hour |
22 | ||
23 | /** @var int Session expiration timeout, in seconds */ | |
24 | public static $LONG_TIMEOUT = 31536000; // 1 year | |
db45a36a | 25 | |
c7721487 | 26 | /** @var array Local reference to the global $_SESSION array */ |
ebd650c0 V |
27 | protected $session = []; |
28 | ||
c7721487 | 29 | /** @var ConfigManager Configuration Manager instance **/ |
49f18323 V |
30 | protected $conf = null; |
31 | ||
51f0128c V |
32 | /** @var bool Whether the user should stay signed in (LONG_TIMEOUT) */ |
33 | protected $staySignedIn = false; | |
34 | ||
c4ad3d4f A |
35 | /** @var string */ |
36 | protected $savePath; | |
37 | ||
ebd650c0 V |
38 | /** |
39 | * Constructor | |
40 | * | |
c4ad3d4f A |
41 | * @param array $session The $_SESSION array (reference) |
42 | * @param ConfigManager $conf ConfigManager instance | |
43 | * @param string $savePath Session save path returned by builtin function session_save_path() | |
ebd650c0 | 44 | */ |
c4ad3d4f | 45 | public function __construct(&$session, $conf, string $savePath) |
ebd650c0 V |
46 | { |
47 | $this->session = &$session; | |
dd883aaf | 48 | $this->conf = $conf; |
c4ad3d4f | 49 | $this->savePath = $savePath; |
ebd650c0 V |
50 | } |
51 | ||
fabff383 A |
52 | /** |
53 | * Initialize XSRF token and links per page session variables. | |
54 | */ | |
55 | public function initialize(): void | |
56 | { | |
57 | if (!isset($this->session['tokens'])) { | |
58 | $this->session['tokens'] = []; | |
59 | } | |
60 | ||
61 | if (!isset($this->session['LINKS_PER_PAGE'])) { | |
62 | $this->session['LINKS_PER_PAGE'] = $this->conf->get('general.links_per_page', 20); | |
63 | } | |
64 | } | |
65 | ||
51f0128c V |
66 | /** |
67 | * Define whether the user should stay signed in across browser sessions | |
68 | * | |
69 | * @param bool $staySignedIn Keep the user signed in | |
70 | */ | |
71 | public function setStaySignedIn($staySignedIn) | |
72 | { | |
73 | $this->staySignedIn = $staySignedIn; | |
74 | } | |
75 | ||
ebd650c0 V |
76 | /** |
77 | * Generates a session token | |
78 | * | |
79 | * @return string token | |
80 | */ | |
81 | public function generateToken() | |
82 | { | |
53054b2b | 83 | $token = sha1(uniqid('', true) . '_' . mt_rand() . $this->conf->get('credentials.salt')); |
ebd650c0 V |
84 | $this->session['tokens'][$token] = 1; |
85 | return $token; | |
86 | } | |
87 | ||
88 | /** | |
89 | * Checks the validity of a session token, and destroys it afterwards | |
90 | * | |
91 | * @param string $token The token to check | |
92 | * | |
93 | * @return bool true if the token is valid, else false | |
94 | */ | |
95 | public function checkToken($token) | |
96 | { | |
97 | if (! isset($this->session['tokens'][$token])) { | |
98 | // the token is wrong, or has already been used | |
99 | return false; | |
100 | } | |
101 | ||
102 | // destroy the token to prevent future use | |
103 | unset($this->session['tokens'][$token]); | |
104 | return true; | |
105 | } | |
fd7d8461 V |
106 | |
107 | /** | |
108 | * Validate session ID to prevent Full Path Disclosure. | |
109 | * | |
110 | * See #298. | |
111 | * The session ID's format depends on the hash algorithm set in PHP settings | |
112 | * | |
113 | * @param string $sessionId Session ID | |
114 | * | |
115 | * @return true if valid, false otherwise. | |
116 | * | |
117 | * @see http://php.net/manual/en/function.hash-algos.php | |
118 | * @see http://php.net/manual/en/session.configuration.php | |
119 | */ | |
120 | public static function checkId($sessionId) | |
121 | { | |
122 | if (empty($sessionId)) { | |
123 | return false; | |
124 | } | |
125 | ||
126 | if (!$sessionId) { | |
127 | return false; | |
128 | } | |
129 | ||
130 | if (!preg_match('/^[a-zA-Z0-9,-]{2,128}$/', $sessionId)) { | |
131 | return false; | |
132 | } | |
133 | ||
134 | return true; | |
135 | } | |
49f18323 V |
136 | |
137 | /** | |
138 | * Store user login information after a successful login | |
139 | * | |
c7721487 | 140 | * @param string $clientIpId Client IP address identifier |
49f18323 | 141 | */ |
c7721487 | 142 | public function storeLoginInfo($clientIpId) |
49f18323 | 143 | { |
c7721487 | 144 | $this->session['ip'] = $clientIpId; |
49f18323 | 145 | $this->session['username'] = $this->conf->get('credentials.login'); |
51f0128c | 146 | $this->extendTimeValidityBy(self::$SHORT_TIMEOUT); |
49f18323 V |
147 | } |
148 | ||
c7721487 V |
149 | /** |
150 | * Extend session validity | |
151 | */ | |
152 | public function extendSession() | |
153 | { | |
51f0128c V |
154 | if ($this->staySignedIn) { |
155 | return $this->extendTimeValidityBy(self::$LONG_TIMEOUT); | |
c7721487 | 156 | } |
51f0128c V |
157 | return $this->extendTimeValidityBy(self::$SHORT_TIMEOUT); |
158 | } | |
159 | ||
160 | /** | |
161 | * Extend expiration time | |
162 | * | |
163 | * @param int $duration Expiration time extension (seconds) | |
164 | * | |
165 | * @return int New session expiration time | |
166 | */ | |
167 | protected function extendTimeValidityBy($duration) | |
168 | { | |
169 | $expirationTime = time() + $duration; | |
170 | $this->session['expires_on'] = $expirationTime; | |
171 | return $expirationTime; | |
c7721487 V |
172 | } |
173 | ||
49f18323 V |
174 | /** |
175 | * Logout a user by unsetting all login information | |
176 | * | |
177 | * See: | |
178 | * - https://secure.php.net/manual/en/function.setcookie.php | |
49f18323 | 179 | */ |
51f0128c | 180 | public function logout() |
49f18323 V |
181 | { |
182 | if (isset($this->session)) { | |
49f18323 | 183 | unset($this->session['ip']); |
51f0128c | 184 | unset($this->session['expires_on']); |
49f18323 V |
185 | unset($this->session['username']); |
186 | unset($this->session['visibility']); | |
49f18323 | 187 | } |
49f18323 | 188 | } |
c7721487 V |
189 | |
190 | /** | |
191 | * Check whether the session has expired | |
192 | * | |
193 | * @param string $clientIpId Client IP address identifier | |
194 | * | |
195 | * @return bool true if the session has expired, false otherwise | |
196 | */ | |
197 | public function hasSessionExpired() | |
198 | { | |
8edd7f15 V |
199 | if (empty($this->session['expires_on'])) { |
200 | return true; | |
201 | } | |
c7721487 V |
202 | if (time() >= $this->session['expires_on']) { |
203 | return true; | |
204 | } | |
205 | return false; | |
206 | } | |
207 | ||
208 | /** | |
209 | * Check whether the client IP address has changed | |
210 | * | |
211 | * @param string $clientIpId Client IP address identifier | |
212 | * | |
213 | * @return bool true if the IP has changed, false if it has not, or | |
214 | * if session protection has been disabled | |
215 | */ | |
216 | public function hasClientIpChanged($clientIpId) | |
217 | { | |
218 | if ($this->conf->get('security.session_protection_disabled') === true) { | |
219 | return false; | |
220 | } | |
8edd7f15 | 221 | if (isset($this->session['ip']) && $this->session['ip'] === $clientIpId) { |
c7721487 V |
222 | return false; |
223 | } | |
224 | return true; | |
225 | } | |
6c50a6cc A |
226 | |
227 | /** @return array Local reference to the global $_SESSION array */ | |
228 | public function getSession(): array | |
229 | { | |
230 | return $this->session; | |
231 | } | |
c266a89d A |
232 | |
233 | /** | |
234 | * @param mixed $default value which will be returned if the $key is undefined | |
235 | * | |
236 | * @return mixed Content stored in session | |
237 | */ | |
238 | public function getSessionParameter(string $key, $default = null) | |
239 | { | |
240 | return $this->session[$key] ?? $default; | |
241 | } | |
af290059 A |
242 | |
243 | /** | |
244 | * Store a variable in user session. | |
245 | * | |
246 | * @param string $key Session key | |
247 | * @param mixed $value Session value to store | |
248 | * | |
249 | * @return $this | |
250 | */ | |
251 | public function setSessionParameter(string $key, $value): self | |
252 | { | |
253 | $this->session[$key] = $value; | |
254 | ||
255 | return $this; | |
256 | } | |
257 | ||
258 | /** | |
259 | * Store a variable in user session. | |
260 | * | |
261 | * @param string $key Session key | |
262 | * | |
263 | * @return $this | |
264 | */ | |
265 | public function deleteSessionParameter(string $key): self | |
266 | { | |
267 | unset($this->session[$key]); | |
268 | ||
269 | return $this; | |
270 | } | |
c4ad3d4f A |
271 | |
272 | public function getSavePath(): string | |
273 | { | |
274 | return $this->savePath; | |
275 | } | |
a8c11451 A |
276 | |
277 | /* | |
278 | * Next public functions wrapping native PHP session API. | |
279 | */ | |
280 | ||
281 | public function destroy(): bool | |
282 | { | |
283 | $this->session = []; | |
284 | ||
285 | return session_destroy(); | |
286 | } | |
287 | ||
288 | public function start(): bool | |
289 | { | |
290 | if (session_status() === PHP_SESSION_ACTIVE) { | |
291 | $this->destroy(); | |
292 | } | |
293 | ||
294 | return session_start(); | |
295 | } | |
296 | ||
d3f6d525 A |
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 | |
a8c11451 | 301 | { |
d3f6d525 | 302 | session_set_cookie_params($lifeTime, $path, $domain); |
a8c11451 A |
303 | } |
304 | ||
305 | public function regenerateId(bool $deleteOldSession = false): bool | |
306 | { | |
307 | return session_regenerate_id($deleteOldSession); | |
308 | } | |
ebd650c0 | 309 | } |