TYPO3 CMS  TYPO3_8-7
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 
18 
23 {
30  private $basePath;
31 
38  private $sessionPath = 'InstallToolSessions/%s';
39 
45  private $cookieName = 'Typo3InstallTool';
46 
52  private $expireTimeInMinutes = 60;
53 
60 
66  public function __construct()
67  {
68  $this->basePath = PATH_site . 'typo3temp/var/';
69  // Start our PHP session early so that hasSession() works
70  $sessionSavePath = $this->getSessionSavePath();
71  // Register our "save" session handler
72  session_set_save_handler([$this, 'open'], [$this, 'close'], [$this, 'read'], [$this, 'write'], [$this, 'destroy'], [$this, 'gc']);
73  session_save_path($sessionSavePath);
74  session_name($this->cookieName);
75  ini_set('session.cookie_httponly', true);
76  ini_set('session.cookie_path', GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
77  // Always call the garbage collector to clean up stale session files
78  ini_set('session.gc_probability', 100);
79  ini_set('session.gc_divisor', 100);
80  ini_set('session.gc_maxlifetime', $this->expireTimeInMinutes * 2 * 60);
81  if (\TYPO3\CMS\Core\Utility\PhpOptionsUtility::isSessionAutoStartEnabled()) {
82  $sessionCreationError = 'Error: session.auto-start is enabled.<br />';
83  $sessionCreationError .= 'The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:<br />';
84  $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
85  throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587485);
86  }
87  if (defined('SID')) {
88  $sessionCreationError = 'Session already started by session_start().<br />';
89  $sessionCreationError .= 'Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.';
90  throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587486);
91  }
92  session_start();
93  }
94 
101  private function getSessionSavePath()
102  {
103  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
104  throw new \TYPO3\CMS\Install\Exception(
105  'No encryption key set to secure session',
106  1371243449
107  );
108  }
109  $sessionSavePath = sprintf(
110  $this->basePath . $this->sessionPath,
111  GeneralUtility::hmac('session:' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])
112  );
113  $this->ensureSessionSavePathExists($sessionSavePath);
114  return $sessionSavePath;
115  }
116 
124  private function ensureSessionSavePathExists($sessionSavePath)
125  {
126  if (!is_dir($sessionSavePath)) {
127  try {
128  GeneralUtility::mkdir_deep($sessionSavePath);
129  } catch (\RuntimeException $exception) {
130  throw new \TYPO3\CMS\Install\Exception(
131  'Could not create session folder in typo3temp/. Make sure it is writeable!',
132  1294587484
133  );
134  }
135  $htaccessContent = '
136 # Apache < 2.3
137 <IfModule !mod_authz_core.c>
138  Order allow,deny
139  Deny from all
140  Satisfy All
141 </IfModule>
142 
143 # Apache ≥ 2.3
144 <IfModule mod_authz_core.c>
145  Require all denied
146 </IfModule>
147  ';
148  GeneralUtility::writeFile($sessionSavePath . '/.htaccess', $htaccessContent);
149  $indexContent = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
150  $indexContent .= '<HTML><HEAD<TITLE></TITLE><META http-equiv=Refresh Content="0; Url=../../">';
151  $indexContent .= '</HEAD></HTML>';
152  GeneralUtility::writeFile($sessionSavePath . '/index.html', $indexContent);
153  }
154  }
155 
161  public function startSession()
162  {
163  $_SESSION['active'] = true;
164  // Be sure to use our own session id, so create a new one
165  return $this->renewSession();
166  }
167 
171  public function destroySession()
172  {
173  session_destroy();
174  }
175 
179  public function resetSession()
180  {
181  $_SESSION = [];
182  $_SESSION['active'] = false;
183  }
184 
190  private function renewSession()
191  {
192  session_regenerate_id();
193  return session_id();
194  }
195 
201  public function hasSession()
202  {
203  return $_SESSION['active'] === true;
204  }
205 
211  public function getSessionId()
212  {
213  return session_id();
214  }
215 
224  private function getSessionHash($sessionId = '')
225  {
226  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
227  throw new \TYPO3\CMS\Install\Exception(
228  'No encryption key set to secure session',
229  1371243450
230  );
231  }
232  if (!$sessionId) {
233  $sessionId = $this->getSessionId();
234  }
235  return md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . '|' . $sessionId);
236  }
237 
244  public function setAuthorized()
245  {
246  $_SESSION['authorized'] = true;
247  $_SESSION['lastSessionId'] = time();
248  $_SESSION['tstamp'] = time();
249  $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
250  // Renew the session id to avoid session fixation
251  $this->renewSession();
252  }
253 
259  public function isAuthorized()
260  {
261  if (!$_SESSION['authorized']) {
262  return false;
263  }
264  if ($_SESSION['expires'] < time()) {
265  // This session has already expired
266  return false;
267  }
268  return true;
269  }
270 
278  public function isExpired()
279  {
280  if (!$_SESSION['authorized']) {
281  // Session never existed, means it is not "expired"
282  return false;
283  }
284  if ($_SESSION['expires'] < time()) {
285  // This session was authorized before, but has expired
286  return true;
287  }
288  return false;
289  }
290 
296  public function refreshSession()
297  {
298  $_SESSION['tstamp'] = time();
299  $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
300  if (time() > $_SESSION['lastSessionId'] + $this->regenerateSessionIdTime * 60) {
301  // Renew our session ID
302  $_SESSION['lastSessionId'] = time();
303  $this->renewSession();
304  }
305  }
306 
312  public function addMessage(\TYPO3\CMS\Install\Status\StatusInterface $message)
313  {
314  if (!is_array($_SESSION['messages'])) {
315  $_SESSION['messages'] = [];
316  }
317  $_SESSION['messages'][] = $message;
318  }
319 
325  public function getMessagesAndFlush()
326  {
327  $messages = [];
328  if (is_array($_SESSION['messages'])) {
329  $messages = $_SESSION['messages'];
330  }
331  $_SESSION['messages'] = [];
332  return $messages;
333  }
334 
335  /*************************
336  *
337  * PHP session handling with "secure" session files (hashed session id)
338  * see http://www.php.net/manual/en/function.session-set-save-handler.php
339  *
340  *************************/
347  private function getSessionFile($id)
348  {
349  $sessionSavePath = $this->getSessionSavePath();
350  return $sessionSavePath . '/hash_' . $this->getSessionHash($id);
351  }
352 
360  public function open($savePath, $sessionName)
361  {
362  return true;
363  }
364 
370  public function close()
371  {
372  return true;
373  }
374 
381  public function read($id)
382  {
383  $sessionFile = $this->getSessionFile($id);
384  $content = '';
385  if (file_exists($sessionFile)) {
386  if ($fd = fopen($sessionFile, 'rb')) {
387  $lockres = flock($fd, LOCK_SH);
388  if ($lockres) {
389  $length = filesize($sessionFile);
390  if ($length > 0) {
391  $content = fread($fd, $length);
392  }
393  flock($fd, LOCK_UN);
394  }
395  fclose($fd);
396  }
397  }
398  // Do a "test write" of the session file after opening it. The real session data is written in
399  // __destruct() and we can not create a sane error message there anymore, so this test should fail
400  // before if final session file can not be written due to permission problems.
401  $this->write($id, $content);
402  return $content;
403  }
404 
413  public function write($id, $sessionData)
414  {
415  $sessionFile = $this->getSessionFile($id);
416  $result = false;
417  $changePermissions = !@is_file($sessionFile);
418  if ($fd = fopen($sessionFile, 'cb')) {
419  if (flock($fd, LOCK_EX)) {
420  ftruncate($fd, 0);
421  $res = fwrite($fd, $sessionData);
422  if ($res !== false) {
423  fflush($fd);
424  $result = true;
425  }
426  flock($fd, LOCK_UN);
427  }
428  fclose($fd);
429  // Change the permissions only if the file has just been created
430  if ($changePermissions) {
431  GeneralUtility::fixPermissions($sessionFile);
432  }
433  }
434  if (!$result) {
435  throw new Exception(
436  'Session file not writable. Please check permission on typo3temp/var/InstallToolSessions and its subdirectories.',
437  1424355157
438  );
439  }
440  return $result;
441  }
442 
449  public function destroy($id)
450  {
451  $sessionFile = $this->getSessionFile($id);
452  return @unlink($sessionFile);
453  }
454 
461  public function gc($maxLifeTime)
462  {
463  $sessionSavePath = $this->getSessionSavePath();
464  $files = glob($sessionSavePath . '/hash_*');
465  if (!is_array($files)) {
466  return true;
467  }
468  foreach ($files as $filename) {
469  if (filemtime($filename) + $this->expireTimeInMinutes * 60 < time()) {
470  @unlink($filename);
471  }
472  }
473  return true;
474  }
475 
487  public function __destruct()
488  {
489  session_write_close();
490  }
491 }
static mkdir_deep($directory, $deepDirectory='')
static hmac($input, $additionalSecret='')
addMessage(\TYPO3\CMS\Install\Status\StatusInterface $message)
static fixPermissions($path, $recursive=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static writeFile($file, $content, $changePermissions=false)