TYPO3  7.6
SessionService.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Install\Service;
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 $typo3tempPath;
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->typo3tempPath = PATH_site . 'typo3temp/';
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(array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc'));
73  session_save_path($sessionSavePath);
74  session_name($this->cookieName);
75  ini_set('session.cookie_path', GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
76  // Always call the garbage collector to clean up stale session files
77  ini_set('session.gc_probability', 100);
78  ini_set('session.gc_divisor', 100);
79  ini_set('session.gc_maxlifetime', $this->expireTimeInMinutes * 2 * 60);
80  if (\TYPO3\CMS\Core\Utility\PhpOptionsUtility::isSessionAutoStartEnabled()) {
81  $sessionCreationError = 'Error: session.auto-start is enabled.<br />';
82  $sessionCreationError .= 'The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:<br />';
83  $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
84  throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587485);
85  } elseif (defined('SID')) {
86  $sessionCreationError = 'Session already started by session_start().<br />';
87  $sessionCreationError .= 'Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.';
88  throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587486);
89  }
90  session_start();
91  }
92 
99  private function getSessionSavePath()
100  {
101  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
102  throw new \TYPO3\CMS\Install\Exception(
103  'No encryption key set to secure session',
104  1371243449
105  );
106  }
107  $sessionSavePath = sprintf(
108  $this->typo3tempPath . $this->sessionPath,
109  GeneralUtility::hmac('session:' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])
110  );
111  $this->ensureSessionSavePathExists($sessionSavePath);
112  return $sessionSavePath;
113  }
114 
122  private function ensureSessionSavePathExists($sessionSavePath)
123  {
124  if (!is_dir($sessionSavePath)) {
125  try {
126  GeneralUtility::mkdir_deep($sessionSavePath);
127  } catch (\RuntimeException $exception) {
128  throw new \TYPO3\CMS\Install\Exception(
129  'Could not create session folder in typo3temp/. Make sure it is writeable!',
130  1294587484
131  );
132  }
133  $htaccessContent = '
134 # Apache < 2.3
135 <IfModule !mod_authz_core.c>
136  Order allow,deny
137  Deny from all
138  Satisfy All
139 </IfModule>
140 
141 # Apache ≥ 2.3
142 <IfModule mod_authz_core.c>
143  Require all denied
144 </IfModule>
145  ';
146  GeneralUtility::writeFile($sessionSavePath . '/.htaccess', $htaccessContent);
147  $indexContent = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
148  $indexContent .= '<HTML><HEAD<TITLE></TITLE><META http-equiv=Refresh Content="0; Url=../../">';
149  $indexContent .= '</HEAD></HTML>';
150  GeneralUtility::writeFile($sessionSavePath . '/index.html', $indexContent);
151  }
152  }
153 
159  public function startSession()
160  {
161  $_SESSION['active'] = true;
162  // Be sure to use our own session id, so create a new one
163  return $this->renewSession();
164  }
165 
169  public function destroySession()
170  {
171  session_destroy();
172  }
173 
177  public function resetSession()
178  {
179  $_SESSION = array();
180  $_SESSION['active'] = false;
181  }
182 
188  private function renewSession()
189  {
190  session_regenerate_id();
191  return session_id();
192  }
193 
199  public function hasSession()
200  {
201  return ($_SESSION['active'] === true);
202  }
203 
209  public function getSessionId()
210  {
211  return session_id();
212  }
213 
222  private function getSessionHash($sessionId = '')
223  {
224  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
225  throw new \TYPO3\CMS\Install\Exception(
226  'No encryption key set to secure session',
227  1371243450
228  );
229  }
230  if (!$sessionId) {
231  $sessionId = $this->getSessionId();
232  }
233  return md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . '|' . $sessionId);
234  }
235 
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 
298  public function refreshSession()
299  {
300  $_SESSION['tstamp'] = time();
301  $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
302  if (time() > $_SESSION['lastSessionId'] + $this->regenerateSessionIdTime * 60) {
303  // Renew our session ID
304  $_SESSION['lastSessionId'] = time();
305  $this->renewSession();
306  }
307  }
308 
315  public function addMessage(\TYPO3\CMS\Install\Status\StatusInterface $message)
316  {
317  if (!is_array($_SESSION['messages'])) {
318  $_SESSION['messages'] = array();
319  }
320  $_SESSION['messages'][] = $message;
321  }
322 
328  public function getMessagesAndFlush()
329  {
330  $messages = array();
331  if (is_array($_SESSION['messages'])) {
332  $messages = $_SESSION['messages'];
333  }
334  $_SESSION['messages'] = array();
335  return $messages;
336  }
337 
338  /*************************
339  *
340  * PHP session handling with "secure" session files (hashed session id)
341  * see http://www.php.net/manual/en/function.session-set-save-handler.php
342  *
343  *************************/
350  private function getSessionFile($id)
351  {
352  $sessionSavePath = $this->getSessionSavePath();
353  return $sessionSavePath . '/hash_' . $this->getSessionHash($id);
354  }
355 
363  public function open($savePath, $sessionName)
364  {
365  return true;
366  }
367 
373  public function close()
374  {
375  return true;
376  }
377 
384  public function read($id)
385  {
386  $sessionFile = $this->getSessionFile($id);
387  $content = (string)(@file_get_contents($sessionFile));
388  // Do a "test write" of the session file after opening it. The real session data is written in
389  // __destruct() and we can not create a sane error message there anymore, so this test should fail
390  // before if final session file can not be written due to permission problems.
391  $this->write($id, $content);
392  return $content;
393  }
394 
403  public function write($id, $sessionData)
404  {
405  $sessionFile = $this->getSessionFile($id);
406  $result = GeneralUtility::writeFile($sessionFile, $sessionData);
407  if (!$result) {
408  throw new Exception(
409  'Session file not writable. Please check permission on typo3temp/InstallToolSessions and its subdirectories.',
410  1424355157
411  );
412  }
413  return $result;
414  }
415 
422  public function destroy($id)
423  {
424  $sessionFile = $this->getSessionFile($id);
425  return @unlink($sessionFile);
426  }
427 
434  public function gc($maxLifeTime)
435  {
436  $sessionSavePath = $this->getSessionSavePath();
437  $files = glob($sessionSavePath . '/hash_*');
438  if (!is_array($files)) {
439  return true;
440  }
441  foreach ($files as $filename) {
442  if (filemtime($filename) + $this->expireTimeInMinutes * 60 < time()) {
443  @unlink($filename);
444  }
445  }
446  return true;
447  }
448 
462  public function __destruct()
463  {
464  session_write_close();
465  }
466 }