Completed
Push — master ( eeeacf...af5244 )
by
unknown
15:28
created

SessionService::isSessionAutoStartEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace TYPO3\CMS\Install\Service;
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
use Symfony\Component\HttpFoundation\Cookie;
19
use TYPO3\CMS\Core\Core\Environment;
20
use TYPO3\CMS\Core\Http\CookieHeaderTrait;
21
use TYPO3\CMS\Core\Messaging\FlashMessage;
22
use TYPO3\CMS\Core\SingletonInterface;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Install\Service\Session\FileSessionHandler;
25
26
/**
27
 * Secure session handling for the install tool.
28
 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
29
 */
30
class SessionService implements SingletonInterface
31
{
32
    use CookieHeaderTrait;
33
34
    /**
35
     * the cookie to store the session ID of the install tool
36
     *
37
     * @var string
38
     */
39
    private $cookieName = 'Typo3InstallTool';
40
41
    /**
42
     * time (minutes) to expire an unused session
43
     *
44
     * @var int
45
     */
46
    private $expireTimeInMinutes = 60;
47
48
    /**
49
     * time (minutes) to generate a new session id for our current session
50
     *
51
     * @var int
52
     */
53
    private $regenerateSessionIdTime = 5;
54
55
    /**
56
     * Constructor. Starts PHP session handling in our own private store
57
     *
58
     * Side-effect: might set a cookie, so must be called before any other output.
59
     */
60
    public function __construct()
61
    {
62
        // Register our "save" session handler
63
        $sessionHandler = GeneralUtility::makeInstance(
64
            FileSessionHandler::class,
65
            Environment::getVarPath() . '/session',
66
            $this->expireTimeInMinutes
67
        );
68
        session_set_save_handler($sessionHandler);
69
        session_name($this->cookieName);
70
        ini_set('session.cookie_httponly', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $newvalue of ini_set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
        ini_set('session.cookie_httponly', /** @scrutinizer ignore-type */ true);
Loading history...
71
        if ($this->hasSameSiteCookieSupport()) {
72
            ini_set('session.cookie_samesite', Cookie::SAMESITE_STRICT);
73
        }
74
        ini_set('session.cookie_path', (string)GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
75
        // Always call the garbage collector to clean up stale session files
76
        ini_set('session.gc_probability', (string)100);
77
        ini_set('session.gc_divisor', (string)100);
78
        ini_set('session.gc_maxlifetime', (string)$this->expireTimeInMinutes * 2 * 60);
79
        if ($this->isSessionAutoStartEnabled()) {
80
            $sessionCreationError = 'Error: session.auto-start is enabled.<br />';
81
            $sessionCreationError .= 'The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:<br />';
82
            $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
83
            throw new \TYPO3\CMS\Install\Exception($sessionCreationError, 1294587485);
84
        }
85
        if (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
        if (!$this->hasSameSiteCookieSupport()) {
92
            $this->resendCookieHeader();
93
        }
94
    }
95
96
    /**
97
     * Starts a new session
98
     *
99
     * @return string The session ID
100
     */
101
    public function startSession()
102
    {
103
        $_SESSION['active'] = true;
104
        // Be sure to use our own session id, so create a new one
105
        return $this->renewSession();
106
    }
107
108
    /**
109
     * Destroys a session
110
     */
111
    public function destroySession()
112
    {
113
        session_destroy();
114
    }
115
116
    /**
117
     * Reset session. Sets _SESSION to empty array.
118
     */
119
    public function resetSession()
120
    {
121
        $_SESSION = [];
122
        $_SESSION['active'] = false;
123
    }
124
125
    /**
126
     * Generates a new session ID and sends it to the client.
127
     *
128
     * @return string the new session ID
129
     */
130
    private function renewSession()
131
    {
132
        session_regenerate_id();
133
        if (!$this->hasSameSiteCookieSupport()) {
134
            $this->resendCookieHeader([$this->cookieName]);
135
        }
136
        return session_id();
137
    }
138
139
    /**
140
     * Checks whether we already have an active session.
141
     *
142
     * @return bool TRUE if there is an active session, FALSE otherwise
143
     */
144
    public function hasSession()
145
    {
146
        return $_SESSION['active'] === true;
147
    }
148
149
    /**
150
     * Marks this session as an "authorized" one (login successful).
151
     * Should only be called if:
152
     * a) we have a valid session running
153
     * b) the "password" or some other authorization mechanism really matched
154
     */
155
    public function setAuthorized()
156
    {
157
        $_SESSION['authorized'] = true;
158
        $_SESSION['lastSessionId'] = time();
159
        $_SESSION['tstamp'] = time();
160
        $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
161
        // Renew the session id to avoid session fixation
162
        $this->renewSession();
163
    }
164
165
    /**
166
     * Marks this session as an "authorized by backend user" one.
167
     * This is called by BackendModuleController from backend context.
168
     */
169
    public function setAuthorizedBackendSession()
170
    {
171
        $_SESSION['authorized'] = true;
172
        $_SESSION['lastSessionId'] = time();
173
        $_SESSION['tstamp'] = time();
174
        $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
175
        $_SESSION['isBackendSession'] = true;
176
        // Renew the session id to avoid session fixation
177
        $this->renewSession();
178
    }
179
180
    /**
181
     * Check if we have an already authorized session
182
     *
183
     * @return bool TRUE if this session has been authorized before (by a correct password)
184
     */
185
    public function isAuthorized()
186
    {
187
        if (!$_SESSION['authorized']) {
188
            return false;
189
        }
190
        if ($_SESSION['expires'] < time()) {
191
            // This session has already expired
192
            return false;
193
        }
194
        return true;
195
    }
196
197
    /**
198
     * Check if we have an authorized session from a system maintainer
199
     *
200
     * @return bool TRUE if this session has been authorized before and initialized by a backend system maintainer
201
     */
202
    public function isAuthorizedBackendUserSession()
203
    {
204
        if (!$_SESSION['authorized'] || !$_SESSION['isBackendSession']) {
205
            return false;
206
        }
207
        if ($_SESSION['expires'] < time()) {
208
            // This session has already expired
209
            return false;
210
        }
211
        return true;
212
    }
213
214
    /**
215
     * Check if our session is expired.
216
     * Useful only right after a FALSE "isAuthorized" to see if this is the
217
     * reason for not being authorized anymore.
218
     *
219
     * @return bool TRUE if an authorized session exists, but is expired
220
     */
221
    public function isExpired()
222
    {
223
        if (!$_SESSION['authorized']) {
224
            // Session never existed, means it is not "expired"
225
            return false;
226
        }
227
        if ($_SESSION['expires'] < time()) {
228
            // This session was authorized before, but has expired
229
            return true;
230
        }
231
        return false;
232
    }
233
234
    /**
235
     * Refreshes our session information, rising the expire time.
236
     * Also generates a new session ID every 5 minutes to minimize the risk of
237
     * session hijacking.
238
     */
239
    public function refreshSession()
240
    {
241
        $_SESSION['tstamp'] = time();
242
        $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
243
        if (time() > $_SESSION['lastSessionId'] + $this->regenerateSessionIdTime * 60) {
244
            // Renew our session ID
245
            $_SESSION['lastSessionId'] = time();
246
            $this->renewSession();
247
        }
248
    }
249
250
    /**
251
     * Add a message to "Flash" message storage.
252
     *
253
     * @param FlashMessage $message A message to add
254
     */
255
    public function addMessage(FlashMessage $message)
256
    {
257
        if (!is_array($_SESSION['messages'])) {
258
            $_SESSION['messages'] = [];
259
        }
260
        $_SESSION['messages'][] = $message;
261
    }
262
263
    /**
264
     * Return stored session messages and flush.
265
     *
266
     * @return FlashMessage[] Messages
267
     */
268
    public function getMessagesAndFlush()
269
    {
270
        $messages = [];
271
        if (is_array($_SESSION['messages'])) {
272
            $messages = $_SESSION['messages'];
273
        }
274
        $_SESSION['messages'] = [];
275
        return $messages;
276
    }
277
278
    /**
279
     * Check if php session.auto_start is enabled
280
     *
281
     * @return bool TRUE if session.auto_start is enabled, FALSE if disabled
282
     */
283
    protected function isSessionAutoStartEnabled()
284
    {
285
        return $this->getIniValueBoolean('session.auto_start');
286
    }
287
288
    /**
289
     * Cast an on/off php ini value to boolean
290
     *
291
     * @param string $configOption
292
     * @return bool TRUE if the given option is enabled, FALSE if disabled
293
     */
294
    protected function getIniValueBoolean($configOption)
295
    {
296
        return filter_var(ini_get($configOption), FILTER_VALIDATE_BOOLEAN, [FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE]);
297
    }
298
}
299