‪TYPO3CMS  9.5
SessionService.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
17 use Symfony\Component\HttpFoundation\Cookie;
24 
30 {
33 
40  private ‪$basePath;
41 
48  private ‪$sessionPath = 'session/%s';
49 
55  private ‪$cookieName = 'Typo3InstallTool';
56 
62  private ‪$expireTimeInMinutes = 15;
63 
69  private ‪$regenerateSessionIdTime = 5;
70 
76  public function ‪__construct()
77  {
78  $this->basePath = ‪Environment::getVarPath() . '/';
79  // Start our PHP session early so that hasSession() works
80  $sessionSavePath = $this->‪getSessionSavePath();
81  // Register our "save" session handler
82  session_set_save_handler([$this, 'open'], [$this, 'close'], [$this, 'read'], [$this, 'write'], [$this, 'destroy'], [$this, 'gc']);
83  session_save_path($sessionSavePath);
84  session_name($this->cookieName);
85  ini_set('session.cookie_httponly', true);
86  if ($this->hasSameSiteCookieSupport()) {
87  ini_set('session.cookie_samesite', Cookie::SAMESITE_STRICT);
88  }
89  ini_set('session.cookie_path', (string)GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
90  // Always call the garbage collector to clean up stale session files
91  ini_set('session.gc_probability', (string)100);
92  ini_set('session.gc_divisor', (string)100);
93  ini_set('session.gc_maxlifetime', (string)$this->expireTimeInMinutes * 2 * 60);
94  if ($this->‪isSessionAutoStartEnabled()) {
95  $sessionCreationError = 'Error: session.auto-start is enabled.<br />';
96  $sessionCreationError .= 'The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:<br />';
97  $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
98  throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587485);
99  }
100  if (session_status() === PHP_SESSION_ACTIVE) {
101  $sessionCreationError = 'Session already started by session_start().<br />';
102  $sessionCreationError .= 'Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.';
103  throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587486);
104  }
105  session_start();
106  if (!$this->hasSameSiteCookieSupport()) {
107  $this->resendCookieHeader([$this->cookieName]);
108  }
109  }
110 
117  private function ‪getSessionSavePath()
118  {
119  if (empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
120  throw new \TYPO3\CMS\Install\Exception(
121  'No encryption key set to secure session',
122  1371243449
123  );
124  }
125  $sessionSavePath = sprintf(
126  $this->basePath . $this->sessionPath,
127  GeneralUtility::hmac('session:' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])
128  );
129  $this->‪ensureSessionSavePathExists($sessionSavePath);
130  return $sessionSavePath;
131  }
132 
140  private function ‪ensureSessionSavePathExists($sessionSavePath)
141  {
142  if (!is_dir($sessionSavePath)) {
143  try {
144  GeneralUtility::mkdir_deep($sessionSavePath);
145  } catch (\RuntimeException $exception) {
146  throw new \TYPO3\CMS\Install\Exception(
147  'Could not create session folder in ' . ‪Environment::getVarPath() . '. Make sure it is writeable!',
148  1294587484
149  );
150  }
151  $htaccessContent = '
152 # Apache < 2.3
153 <IfModule !mod_authz_core.c>
154  Order allow,deny
155  Deny from all
156  Satisfy All
157 </IfModule>
158 
159 # Apache ≥ 2.3
160 <IfModule mod_authz_core.c>
161  Require all denied
162 </IfModule>
163  ';
164  GeneralUtility::writeFile($sessionSavePath . '/.htaccess', $htaccessContent);
165  $indexContent = '<!DOCTYPE html>';
166  $indexContent .= '<html><head><title></title><meta http-equiv=Refresh Content="0; Url=../../"/>';
167  $indexContent .= '</head></html>';
168  GeneralUtility::writeFile($sessionSavePath . '/index.html', $indexContent);
169  }
170  }
171 
177  public function ‪startSession()
178  {
179  $_SESSION['active'] = true;
180  // Be sure to use our own session id, so create a new one
181  return $this->‪renewSession();
182  }
183 
187  public function ‪destroySession()
188  {
189  session_destroy();
190  }
191 
195  public function ‪resetSession()
196  {
197  $_SESSION = [];
198  $_SESSION['active'] = false;
199  }
200 
206  private function ‪renewSession()
207  {
208  session_regenerate_id();
209  if (!$this->hasSameSiteCookieSupport()) {
210  $this->resendCookieHeader([$this->cookieName]);
211  }
212  return session_id();
213  }
214 
220  public function ‪hasSession()
221  {
222  return $_SESSION['active'] === true;
223  }
224 
230  public function ‪getSessionId()
231  {
232  return session_id();
233  }
234 
243  private function ‪getSessionHash($sessionId = '')
244  {
245  if (empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
246  throw new \TYPO3\CMS\Install\Exception(
247  'No encryption key set to secure session',
248  1371243450
249  );
250  }
251  if (!$sessionId) {
252  $sessionId = $this->‪getSessionId();
253  }
254  return md5(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . '|' . $sessionId);
255  }
256 
263  public function ‪setAuthorized()
264  {
265  $_SESSION['authorized'] = true;
266  $_SESSION['lastSessionId'] = time();
267  $_SESSION['tstamp'] = time();
268  $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
269  // Renew the session id to avoid session fixation
270  $this->‪renewSession();
271  }
272 
277  public function ‪setAuthorizedBackendSession()
278  {
279  $_SESSION['authorized'] = true;
280  $_SESSION['lastSessionId'] = time();
281  $_SESSION['tstamp'] = time();
282  $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
283  $_SESSION['isBackendSession'] = true;
284  // Renew the session id to avoid session fixation
285  $this->‪renewSession();
286  }
287 
293  public function ‪isAuthorized()
294  {
295  if (!$_SESSION['authorized']) {
296  return false;
297  }
298  if ($_SESSION['expires'] < time()) {
299  // This session has already expired
300  return false;
301  }
302  return true;
303  }
304 
310  public function ‪isAuthorizedBackendUserSession()
311  {
312  if (!$_SESSION['authorized'] || !$_SESSION['isBackendSession']) {
313  return false;
314  }
315  if ($_SESSION['expires'] < time()) {
316  // This session has already expired
317  return false;
318  }
319  return true;
320  }
321 
329  public function ‪isExpired()
330  {
331  if (!$_SESSION['authorized']) {
332  // Session never existed, means it is not "expired"
333  return false;
334  }
335  if ($_SESSION['expires'] < time()) {
336  // This session was authorized before, but has expired
337  return true;
338  }
339  return false;
340  }
341 
347  public function ‪refreshSession()
348  {
349  $_SESSION['tstamp'] = time();
350  $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
351  if (time() > $_SESSION['lastSessionId'] + $this->regenerateSessionIdTime * 60) {
352  // Renew our session ID
353  $_SESSION['lastSessionId'] = time();
354  $this->‪renewSession();
355  }
356  }
357 
363  public function ‪addMessage(FlashMessage $message)
364  {
365  if (!is_array($_SESSION['messages'])) {
366  $_SESSION['messages'] = [];
367  }
368  $_SESSION['messages'][] = $message;
369  }
370 
376  public function ‪getMessagesAndFlush()
377  {
378  $messages = [];
379  if (is_array($_SESSION['messages'])) {
380  $messages = $_SESSION['messages'];
381  }
382  $_SESSION['messages'] = [];
383  return $messages;
384  }
385 
386  /*************************
387  *
388  * PHP session handling with "secure" session files (hashed session id)
389  * see http://www.php.net/manual/en/function.session-set-save-handler.php
390  *
391  *************************/
398  private function ‪getSessionFile($id)
399  {
400  $sessionSavePath = $this->‪getSessionSavePath();
401  return $sessionSavePath . '/hash_' . $this->‪getSessionHash($id);
402  }
403 
411  public function ‪open($savePath, $sessionName)
412  {
413  return true;
414  }
415 
421  public function ‪close()
422  {
423  return true;
424  }
425 
432  public function ‪read($id)
433  {
434  $sessionFile = $this->‪getSessionFile($id);
435  $content = '';
436  if (file_exists($sessionFile)) {
437  if ($fd = fopen($sessionFile, 'rb')) {
438  $lockres = flock($fd, LOCK_SH);
439  if ($lockres) {
440  $length = filesize($sessionFile);
441  if ($length > 0) {
442  $content = fread($fd, $length);
443  }
444  flock($fd, LOCK_UN);
445  }
446  fclose($fd);
447  }
448  }
449  // Do a "test write" of the session file after opening it. The real session data is written in
450  // __destruct() and we can not create a sane error message there anymore, so this test should fail
451  // before if final session file can not be written due to permission problems.
452  $this->‪write($id, $content);
453  return $content;
454  }
455 
464  public function ‪write($id, $sessionData)
465  {
466  $sessionFile = $this->‪getSessionFile($id);
467  $result = false;
468  $changePermissions = !@is_file($sessionFile);
469  if ($fd = fopen($sessionFile, 'cb')) {
470  if (flock($fd, LOCK_EX)) {
471  ftruncate($fd, 0);
472  $res = fwrite($fd, $sessionData);
473  if ($res !== false) {
474  fflush($fd);
475  $result = true;
476  }
477  flock($fd, LOCK_UN);
478  }
479  fclose($fd);
480  // Change the permissions only if the file has just been created
481  if ($changePermissions) {
482  GeneralUtility::fixPermissions($sessionFile);
483  }
484  }
485  if (!$result) {
486  throw new Exception(
487  'Session file not writable. Please check permission on ' .
488  ‪Environment::getVarPath() . '/session and its subdirectories.',
489  1424355157
490  );
491  }
492  return $result;
493  }
494 
501  public function ‪destroy($id)
502  {
503  $sessionFile = $this->‪getSessionFile($id);
504  return @unlink($sessionFile);
505  }
506 
513  public function ‪gc($maxLifeTime)
514  {
515  $sessionSavePath = $this->‪getSessionSavePath();
516  $files = glob($sessionSavePath . '/hash_*');
517  if (!is_array($files)) {
518  return true;
519  }
520  foreach ($files as $filename) {
521  if (filemtime($filename) + $this->expireTimeInMinutes * 60 < time()) {
522  @unlink($filename);
523  }
524  }
525  return true;
526  }
527 
539  public function ‪__destruct()
540  {
541  session_write_close();
542  }
543 
549  protected function ‪isSessionAutoStartEnabled()
550  {
551  return $this->‪getIniValueBoolean('session.auto_start');
552  }
553 
560  protected function ‪getIniValueBoolean($configOption)
561  {
562  return filter_var(ini_get($configOption), FILTER_VALIDATE_BOOLEAN, [FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE]);
563  }
564 }
‪TYPO3\CMS\Install\Service\SessionService\addMessage
‪addMessage(FlashMessage $message)
Definition: SessionService.php:358
‪TYPO3\CMS\Install\Service\SessionService\getIniValueBoolean
‪bool getIniValueBoolean($configOption)
Definition: SessionService.php:555
‪TYPO3\CMS\Install\Service\SessionService\getSessionSavePath
‪string getSessionSavePath()
Definition: SessionService.php:112
‪TYPO3\CMS\Install\Service\SessionService\hasSession
‪bool hasSession()
Definition: SessionService.php:215
‪TYPO3\CMS\Install\Service\SessionService\getSessionFile
‪string getSessionFile($id)
Definition: SessionService.php:393
‪TYPO3\CMS\Install\Service\SessionService\close
‪bool close()
Definition: SessionService.php:416
‪TYPO3\CMS\Install\Service\SessionService\write
‪bool write($id, $sessionData)
Definition: SessionService.php:459
‪TYPO3\CMS\Install\Service\SessionService\setAuthorizedBackendSession
‪setAuthorizedBackendSession()
Definition: SessionService.php:272
‪TYPO3\CMS\Install\Service\SessionService\resetSession
‪resetSession()
Definition: SessionService.php:190
‪TYPO3\CMS\Install\Service\SessionService\destroy
‪string destroy($id)
Definition: SessionService.php:496
‪TYPO3\CMS\Core\Security\BlockSerializationTrait
Definition: BlockSerializationTrait.php:28
‪TYPO3\CMS\Install\Service\SessionService\isSessionAutoStartEnabled
‪bool isSessionAutoStartEnabled()
Definition: SessionService.php:544
‪TYPO3\CMS\Install\Service\SessionService\refreshSession
‪refreshSession()
Definition: SessionService.php:342
‪TYPO3\CMS\Install\Service\SessionService\startSession
‪string startSession()
Definition: SessionService.php:172
‪TYPO3\CMS\Install\Service\SessionService\__construct
‪__construct()
Definition: SessionService.php:71
‪TYPO3\CMS\Install\Service\SessionService\getMessagesAndFlush
‪FlashMessage[] getMessagesAndFlush()
Definition: SessionService.php:371
‪TYPO3\CMS\Install\Service\SessionService\$sessionPath
‪string $sessionPath
Definition: SessionService.php:46
‪TYPO3\CMS\Install\Service\SessionService\__destruct
‪__destruct()
Definition: SessionService.php:534
‪TYPO3\CMS\Install\Service\SessionService\setAuthorized
‪setAuthorized()
Definition: SessionService.php:258
‪TYPO3\CMS\Install\Service\SessionService\getSessionHash
‪string getSessionHash($sessionId='')
Definition: SessionService.php:238
‪TYPO3\CMS\Install\Service\SessionService\$cookieName
‪string $cookieName
Definition: SessionService.php:52
‪TYPO3\CMS\Install\Service\SessionService\isExpired
‪bool isExpired()
Definition: SessionService.php:324
‪TYPO3\CMS\Install\Service\SessionService\$expireTimeInMinutes
‪int $expireTimeInMinutes
Definition: SessionService.php:58
‪TYPO3\CMS\Install\Service\SessionService\isAuthorized
‪bool isAuthorized()
Definition: SessionService.php:288
‪TYPO3\CMS\Install\Service\SessionService\open
‪bool open($savePath, $sessionName)
Definition: SessionService.php:406
‪TYPO3\CMS\Install\Service\SessionService\getSessionId
‪string getSessionId()
Definition: SessionService.php:225
‪TYPO3\CMS\Install\Service\SessionService\destroySession
‪destroySession()
Definition: SessionService.php:182
‪TYPO3\CMS\Install\Service\SessionService\renewSession
‪string renewSession()
Definition: SessionService.php:201
‪TYPO3\CMS\Install\Service\SessionService\$basePath
‪string $basePath
Definition: SessionService.php:39
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:22
‪TYPO3\CMS\Install\Service\SessionService\read
‪string read($id)
Definition: SessionService.php:427
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Install\Service\SessionService\isAuthorizedBackendUserSession
‪bool isAuthorizedBackendUserSession()
Definition: SessionService.php:305
‪TYPO3\CMS\Install\Service\SessionService\ensureSessionSavePathExists
‪ensureSessionSavePathExists($sessionSavePath)
Definition: SessionService.php:135
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Install\Service\SessionService\gc
‪bool gc($maxLifeTime)
Definition: SessionService.php:508
‪TYPO3\CMS\Install\Service
Definition: ClearCacheService.php:2
‪TYPO3\CMS\Install\Service\SessionService
Definition: SessionService.php:30
‪TYPO3\CMS\Install\Service\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Install\Service\SessionService\$regenerateSessionIdTime
‪int $regenerateSessionIdTime
Definition: SessionService.php:64
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:165