Issues (1)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Session.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
5
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
12
 * @link          https://cakephp.org CakePHP(tm) Project
13
 * @since         0.10.0
14
 * @license       https://opensource.org/licenses/mit-license.php MIT License
15
 */
16
17
declare(strict_types=1);
18
19
namespace Phauthentic\Infrastructure\Http\Session;
20
21
use Adbar\Dot;
22
use SessionHandlerInterface;
23
24
/**
25
 * This class is a wrapper for the native PHP session functions. It provides
26
 * several defaults for the most common session configuration
27
 * via external handlers and helps with using session in cli without any warnings.
28
 *
29
 * Sessions can be created from the defaults using `Session::create()` or you can get
30
 * an instance of a new session by just instantiating this class and passing the complete
31
 * options you want to use.
32
 *
33
 * When specific options are omitted, this class will take its defaults from the configuration
34
 * values from the `session.*` directives in php.ini. This class will also alter such
35
 * directives when configuration values are provided.
36
 */
37
class Session implements SessionInterface
38
{
39
    /**
40
     * The Session handler instance used as an engine for persisting the session data.
41
     *
42
     * @var \SessionHandlerInterface
43
     */
44
    protected SessionHandlerInterface $handler;
45
46
    /**
47
     * The Session handler instance used as an engine for persisting the session data.
48
     *
49
     * @var \Phauthentic\Infrastructure\Http\Session\ConfigInterface
50
     */
51
    protected ConfigInterface $config;
52
53
    /**
54
     * Indicates whether the sessions has already started
55
     *
56
     * @var bool
57
     */
58
    protected bool $started = false;
59
60
    /**
61
     * The time in seconds the session will be valid for
62
     *
63
     * @var int
64
     */
65
    protected int $lifetime = 0;
66
67
    /**
68
     * Whether this session is running under a CLI environment
69
     *
70
     * @var bool
71
     */
72
    protected bool $isCli = false;
73
74
    /**
75
     * Constructor.
76
     * ### Configuration:
77
     * - timeout: The time in minutes the session should be valid for.
78
     * - cookiePath: The url path for which session cookie is set. Maps to the
79
     *   `session.cookie_path` php.ini config. Defaults to base path of app.
80
     * - ini: A list of php.ini directives to change before the session start.
81
     * - handler: An array containing at least the `class` key. To be used as the session
82
     *   engine for persisting data. The rest of the keys in the array will be passed as
83
     *   the configuration array for the engine. You can set the `class` key to an already
84
     *   instantiated session handler object.
85
     *
86
     * @param \Phauthentic\Infrastructure\Http\Session\ConfigInterface|null $config The Configuration to apply to this session object
87
     * @param \SessionHandlerInterface|null $handler
88
     */
89 1
    public function __construct(
90
        ?ConfigInterface $config = null,
91
        ?SessionHandlerInterface $handler = null
92
    ) {
93 1
        if ($config !== null) {
94
            $this->config = $config;
95
        } else {
96 1
            $this->config = new Config();
97 1
            $this->config->setUseTransSid(false);
98
        }
99
100 1
        if ($handler !== null) {
101
            $this->setSaveHandler($handler);
102
        }
103
104 1
        $this->lifetime = (int)ini_get('session.gc_maxlifetime');
105 1
        $this->isCli = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg');
106
107 1
        session_register_shutdown();
108 1
    }
109
110
    /**
111
     * @return \Phauthentic\Infrastructure\Http\Session\ConfigInterface
112
     */
113
    public function config(): ConfigInterface
114
    {
115
        return $this->config;
116
    }
117
118
    /**
119
     * Set the engine property and update the session handler in PHP.
120
     *
121
     * @param \SessionHandlerInterface $handler The handler to set
122
     * @return void
123
     */
124
    protected function setSaveHandler(SessionHandlerInterface $handler): void
125
    {
126
        if (!headers_sent()) {
127
            session_set_save_handler($handler, false);
128
        }
129
130
        $this->handler = $handler;
131
    }
132
133
    /**
134
     * Startup checks
135
     *
136
     * @return bool
137
     */
138
    protected function doStartupChecks(): bool
139
    {
140
        if (session_status() === PHP_SESSION_ACTIVE) {
141
            throw SessionException::alreadyStarted();
142
        }
143
144
        if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
145
            return false;
146
        }
147
148
        if (!session_start()) {
149
            throw SessionException::couldNotStart();
150
        }
151
152
        return true;
153
    }
154
155
    /**
156
     * Starts the Session.
157
     *
158
     * @return bool True if session was started
159
     * @throws \Phauthentic\Infrastructure\Http\Session\SessionException if the session was already started
160
     */
161 1
    public function start(): bool
162
    {
163 1
        if ($this->started) {
164
            return true;
165
        }
166
167 1
        if ($this->isCli) {
168 1
            $_SESSION = [];
169 1
            $this->setId('cli');
170
171 1
            return $this->started = true;
172
        }
173
174
        if (!$this->doStartupChecks()) {
175
            return false;
176
        }
177
178
        $this->started = true;
179
180
        if ($this->hasExpired()) {
181
            $this->destroy();
182
183
            return $this->start();
184
        }
185
186
        return $this->started;
187
    }
188
189
    /**
190
     * @return array|string|null
191
     */
192
    protected function time()
193
    {
194
        return $this->read('Config.time');
195
    }
196
197
    /**
198
     * Determine if Session has already been started.
199
     *
200
     * @return bool True if session has been started.
201
     */
202 1
    public function started(): bool
203
    {
204 1
        return $this->started || session_status() === PHP_SESSION_ACTIVE;
205
    }
206
207
    /**
208
     * Returns true if given variable name is set in session.
209
     *
210
     * @param string|null $name Variable name to check for
211
     * @return bool True if variable is there
212
     */
213 1
    public function check(?string $name = null): bool
214
    {
215 1
        if ($this->exists() && !$this->started()) {
216
            $this->start();
217
        }
218
219 1
        if (!isset($_SESSION)) {
220
            return false;
221
        }
222
223 1
        return (new Dot($_SESSION))->get($name) !== null;
224
    }
225
226
    /**
227
     * Returns given session variable, or all of them, if no parameters given.
228
     *
229
     * @param string|null $name The name of the session variable (or a path as sent to Hash.extract)
230
     * @return string|array|null The value of the session variable, null if session not available,
231
     *   session not started, or provided name not found in the session.
232
     */
233 1
    public function read(?string $name = null)
234
    {
235 1
        if ($this->exists() && !$this->started()) {
236
            $this->start();
237
        }
238
239 1
        if (!isset($_SESSION)) {
240
            return null;
241
        }
242
243 1
        if ($name === null) {
244
            return $_SESSION ?? [];
245
        }
246
247 1
        return (new Dot($_SESSION))->get($name);
248
    }
249
250
    /**
251
     * Reads and deletes a variable from session.
252
     *
253
     * @param string $name The key to read and remove (or a path as sent to Hash.extract).
254
     * @return mixed The value of the session variable, null if session not available,
255
     *   session not started, or provided name not found in the session.
256
     */
257 1
    public function consume(string $name)
258
    {
259 1
        if (empty($name)) {
260
            return null;
261
        }
262
263 1
        $value = $this->read($name);
264 1
        if ($value !== null) {
265 1
            $dot = new Dot($_SESSION);
266 1
            $dot->delete($name);
267 1
            $this->overwrite($_SESSION, (array)$dot->get());
268
        }
269
270 1
        return $value;
271
    }
272
273
    /**
274
     * Writes value to given session variable name.
275
     *
276
     * @param string|array $name Name of variable
277
     * @param mixed $value Value to write
278
     * @return void
279
     */
280 1
    public function write($name, $value = null): void
281
    {
282 1
        if (!$this->started()) {
283
            $this->start();
284
        }
285
286 1
        $write = $name;
287 1
        if (!is_array($name)) {
288 1
            $write = [$name => $value];
289
        }
290
291 1
        $data = new Dot($_SESSION ?? []);
292 1
        foreach ($write as $key => $val) {
293 1
            $data->add($key, $val);
294
        }
295
296 1
        $this->overwrite($_SESSION, $data->get());
297 1
    }
298
299
    /**
300
     * Returns the current sessions id
301
     *
302
     * @return string
303
     */
304
    public function id(): string
305
    {
306
        return (string)session_id();
307
    }
308
309
    /**
310
     * Sets the session id
311
     *
312
     * Calling this method will not auto start the session. You might have to manually
313
     * assert a started session.
314
     *
315
     * Passing an id into it, you can also replace the session id if the session
316
     * has not already been started.
317
     *
318
     * Note that depending on the session handler, not all characters are allowed
319
     * within the session id. For example, the file session handler only allows
320
     * characters in the range a-z A-Z 0-9 , (comma) and - (minus).
321
     *
322
     * @param string $id Session Id
323
     * @return $this
324
     */
325 1
    public function setId(string $id): self
326
    {
327 1
        if (headers_sent()) {
328
            throw SessionException::headersAlreadySent();
329
        }
330
331 1
        session_id($id);
332
333 1
        return $this;
334
    }
335
336
    /**
337
     * Removes a variable from session.
338
     *
339
     * @param string $name Session variable to remove
340
     * @return void
341
     */
342 1
    public function delete(string $name): void
343
    {
344 1
        if ($this->check($name)) {
345 1
            $this->overwrite($_SESSION, (array)(new Dot($_SESSION))->delete($name));
0 ignored issues
show
Are you sure the usage of new Adbar\Dot($_SESSION)->delete($name) targeting Adbar\Dot::delete() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
346
        }
347 1
    }
348
349
    /**
350
     * Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself.
351
     *
352
     * @param array $old Set of old variables => values
353
     * @param array $new New set of variable => value
354
     * @return void
355
     */
356 1
    protected function overwrite(&$old, $new)
357
    {
358 1
        if (!empty($old)) {
359 1
            foreach ($old as $key => $var) {
360 1
                if (!isset($new[$key])) {
361 1
                    unset($old[$key]);
362
                }
363
            }
364
        }
365
366 1
        foreach ($new as $key => $var) {
367 1
            $old[$key] = $var;
368
        }
369 1
    }
370
371
    /**
372
     * Helper method to destroy invalid sessions.
373
     *
374
     * @return void
375
     */
376
    public function destroy()
377
    {
378
        if ($this->exists() && !$this->started()) {
379
            $this->start();
380
        }
381
382
        if (!$this->isCli && session_status() === PHP_SESSION_ACTIVE) {
383
            session_destroy();
384
        }
385
386
        $_SESSION = [];
387
        $this->started = false;
388
    }
389
390
    /**
391
     * Clears the session.
392
     *
393
     * Optionally it also clears the session id and renews the session.
394
     *
395
     * @param bool $renew If session should be renewed, as well. Defaults to false.
396
     * @return void
397
     */
398
    public function clear(bool $renew = false)
399
    {
400
        $_SESSION = [];
401
        if ($renew) {
402
            $this->renew();
403
        }
404
    }
405
406
    /**
407
     * Returns whether a session exists
408
     *
409
     * @return bool
410
     */
411 1
    public function exists()
412
    {
413 1
        return !ini_get('session.use_cookies')
414 1
            || isset($_COOKIE[session_name()])
415 1
            || $this->isCli
416 1
            || (ini_get('session.use_trans_sid') && isset($_GET[session_name()]));
417
    }
418
419
    /**
420
     * Restarts this session.
421
     *
422
     * @return void
423
     */
424
    public function renew(): void
425
    {
426
        if (!$this->exists() || $this->isCli) {
427
            return;
428
        }
429
430
        $this->start();
431
        $params = session_get_cookie_params();
432
        setcookie(
433
            session_name(),
434
            '',
435
            time() - 42000,
436
            $params['path'],
437
            $params['domain'],
438
            $params['secure'],
439
            $params['httponly']
440
        );
441
442
        if (session_id()) {
443
            session_regenerate_id(true);
444
        }
445
    }
446
447
    /**
448
     * Returns true if the session is no longer valid because the last time it was
449
     * accessed was after the configured timeout.
450
     *
451
     * @return bool
452
     */
453
    public function hasExpired(): bool
454
    {
455
        $time = $this->time();
456
        $result = false;
457
458
        $checkTime = $time !== null && $this->lifetime > 0;
459
        if ($checkTime && (time() - (int)$time > $this->lifetime)) {
460
            $result = true;
461
        }
462
463
        $this->write('Config.time', time());
464
465
        return $result;
466
    }
467
}
468